As mentioned on www.netbsd.org the german publisher C&L
has made available a book on NetBSD 1.6. The book
covers NetBSD installation (i386, macppc and others), user administration, updating
the system, kernel tuning, configuring the rc.d startup system, package
management using pkg_install and the pkgsrc tree, configuring the X window system
and using NetBSD in a networked environment (IPv4/6, DNS, NFS/NIS, FTP, Samba and the like).
Loadable kernel modules (LKMs) are quite popular on most modern operating systems such as
GNU/Linux, FreeBSD and of course
Microsoft Windows, just to name a few. They offer you the possibility to extend the kernel’s
functionality at runtime without recompiling or even rebooting the system. For example nearly
every Linux device driver is available - or can be made available - as a loadable kernel module,
that can be loaded at runtime to get support for a particular device (or even a pseudo-device).
With NetBSD, LKMs are not that popular yet. At the time of this writing
only a few drivers are available as loadable modules (mostly filesystem and compat drivers, and
a few others such as the linuxrtc emulation). This might change in near future.
The loadable kernel module interface was originally designed to be similar in functionality to the
loadable kernel modules facility provided by SunOS. The lkm(4)
facility is controlled by performing ioctl(2)
calls on the /dev/lkm device, but since all operations are handled by the
modload(8),
modunload(8) and
modstat(8) programs, you should never
have to interact with /dev/lkm directly. Note, that you need to run a kernel compiled
with the LKM option in order to make use of LKMs.
Writing the module
I’d like to show you how to write a simple character device driver that does nothing but the simple job of
calculating the Fibonacci numbers (I’ll therefore name
the module fibo.o and let all the function’s names begin with fibo_). The driver
will provide 8 minor devices /dev/fibo0 to /dev/fibo7.
Each minor device offers the following functions:
You can open and close a device provided by this driver and you’ll be able to read from it (we’ll have
a closer look at the parameters later, when we discuss the actual functions). Now we need to tell the
kernel that we provide a character device with the 3 functions listed above. Therefore we need to fill
in a struct cdevsw (cdevsw means character device switch and the struct cdevsw
is defined in sys/conf.h).
enodev is a generic function that simply returns the
errno(2)ENODEV
(Operation not supported by device) which says that we does not support any operations besides
open, close and read. So, for example, whenever you try to write to the device, the
write(2) will fail with ENODEV.
Furtheron we need to tell the kernel how the module is named and where to find information about
operations provided by the module. This is a quite simple task with the lkm interface: We use the
preprocessor macro MOD_DEV, which is defined in sys/lkm.h to hand the
information over. The MOD_DEV macro was changed in NetBSD-current, therefore we use
the following construct to get things working with both NetBSD 1.6 and earlier and NetBSD 1.6H and
later (thanks to Anil Gopinath for the hint).
This means that our module is named fibo, we’ll provide a character device (minor devices
are handled by the module itself, so they doesn’t matter for now), we want to retrieve a dynamic major
device number from the kernel (if you want to use a specific major device number you’ll need to specify
that instead of the -1, but beware of getting in conflict with other device drivers) and we
provide the information about the supported operations in fibo_dev.
In order to ensure proper unloading of the module we need to keep a global reference counter of opened
minor devices.
staticintfibo_refcnt=0;
And furtheron we need to keep a bunch of information about each minor device.
As mentioned above our driver will provide 8 minor devices. Each minor device stores information about
how often it was opened (in our example each minor device can only be opened once to keep things simple),
the current number and the previous number for calculating the Fibonacci numbers. If you don’t know how
to calculate the Fibonacci numbers, you should have a look on a book about algorithms, as explaining this
is beyond the scope of this article.
Each kernel module needs to have an entry point which is passed to
ld(1) by modload when the module is linked. The
default module entry point is named xxxinit. If xxxinit cannot be found, an
attempt to use modulename_lkmentry will be made, where modulename
is the filename of the module being loaded without the .o. In general the entry function will
consist entirely of a single DISPATCH line, with DISPATCH being a preprocessor
macro defined in sys/lkm.h to handle loading, unloading and stating for us. So our
fibo_lkmentry function will look like this:
Now we need a handler function for our module to do module specific tasks when loading, unloading or stating
the module. The name of this handler function is passed to DISPATCH (see above) to tell the
kernel which function it has to call when doing such things. A pointer to the module entry in the LKM table
and an integer representing the desired command (LKM_E_LOAD, LKM_E_UNLOAD or
LKM_E_STAT) are passed to the handler function. The handler is called after the module is
linked and loaded into the kernel with the LKM_E_LOAD command. Then we need to check whether
the module was already loaded into the kernel and initialize our data structures. When unloading the module,
the handler is called with the LKM_E_UNLOAD command and we need to check if the module is not
required any more (e.g. check if all devices are closed for char/block driver modules) before confirming the
unload command.
staticintfibo_handle(structlkm_table*lkmtp,intcmd){switch(cmd){caseLKM_E_LOAD:/* check if module was already loaded */if(lkmexists(lkmtp))return(EEXIST);/* initialize minor device structures */bzero(fibo_scs,sizeof(fibo_scs));printf("fibo: FIBONACCI driver loaded successfully\n");break;caseLKM_E_UNLOAD:/* check if a minor device is opened */if(fibo_refcnt>0)return(EBUSY);break;caseLKM_E_STAT:break;default:return(EIO);}return(0);}
The open function is quite simple as most of the hard stuff is already handled by the NetBSD kernel
(e.g. the kernel will automatically allocate a vnode(9)
for you). The parameters for the open function are the major and minor device numbers (use the major
and minor macros), the flag and mode arguments as described in
open(2) and a pointer to the struct proc
of the process that did the open system call.
So the first thing to do is to check if the minor device number we got when the device was opened is not out of
range, and if the minor device is not already opened. You should always keep in mind that the minor device
handling is completely up to you and that this is a never ending source of mistakes! Then we need to initialize
the minor device data (the Fibonacci starting numbers 1, 0 + 1 = 1, 1 + 1 = 2, 1 + 2 = 3, …) and increase the
minor device and the global module reference counter.
staticintfibo_open(dev_tdev,intflag,intmode,structproc*p){structfibo_softc*fibosc=(fibo_scs+minor(dev));if(minor(dev)>=MAXFIBODEVS)return(ENODEV);/* check if device already open */if(fibosc->sc_refcnt>0)return(EBUSY);fibosc->sc_current=1;fibosc->sc_previous=0;/* increase device reference counter */fibosc->sc_refcnt++;/* increase module reference counter */fibo_refcnt++;return(0);}
The close function has the same parameters with the same meanings as the open function described above. It
is used to free the internal data structures of a minor device opened before. You do not need to worry whether
the device was opened before or to do things like releasing the vnode associated with the device, all you need
to do is to cleanup the module specific stuff. In our example this means decreasing the minor device and the
global module reference counters and so that our close function is quite simple.
Last but not least the read function. This function has 3 parameters: the device major and minor numbers like
in the open and close functions, a flag field indicating for example whether the read should be
done in a non-blocking fashion or such things and a pointer to a struct uio defined in
sys/uio.h. A struct uio typically describes data in motion, in case of a
read(2) system call data moved from kernel-space
to user-space. This may look a bit strange if you already did device driver progamming on GNU/Linux, but the
uio concept used by the NetBSD kernel simplifies a lot of things and provides a generic and consistent interface
for kernel-space to user-space and kernel-space to kernel-space data moving. See
uiomove(9) for more information.
Back on stage, we should first have a look at the read function and discuss the details afterwards.
staticintfibo_read(dev_tdev,structuio*uio,intflag){structfibo_softc*fibosc=(fibo_scs+minor(dev));if(uio->uio_resid<sizeof(u_int32_t))return(EINVAL);while(uio->uio_resid>=sizeof(u_int32_t)){interror;/* copy to user space */if((error=uiomove(&(fibosc->sc_current),sizeof(fibosc->sc_current),uio))){return(error);}/* prevent overflow */if(fibosc->sc_current>(MAXFIBONUM-1)){fibosc->sc_current=1;fibosc->sc_previous=0;continue;}/* calculate */{u_int32_ttmp;tmp=fibosc->sc_current;fibosc->sc_current+=fibosc->sc_previous;fibosc->sc_previous=tmp;}}return(0);}
So the first thing we do, is to check whether the process requests less than sizeof(u_int32_t)
bytes (actually 4 bytes). Our read function always reads a bunch of 4-byte blocks and to keep it simple
and easy to understand we disallow reading less than 4 bytes at a time (uio->uio_resid holds
the number of remaining bytes to move to user-space, automatically decreased by uiomove).
The function copies the current Fibonacci number into the user-space buffer, checks for a possible overflow
(only the first 42 Fibonacci numbers fit into u_int32_t) and calculates the next Fibonacci
number. If there is enough space left in the user-space buffer, the function loops and restarts the process
of moving, checking and calculating until the buffer is filled up to the possible maximum or
uiomove(9) returns an error. Note, that a
read(2) system call on this device will never
return 0, and so it will never reach an end-of-file (EOF), so the device will generate Fibonacci numbers
forever.
If you’re familar with GNU/Linux device driver programming you might have noticed that we do not return
-ERRNO on failure, and in case of the read system call the number of bytes read, but
instead we return 0 on success and the positive errno value on failure. Everything else is
handled by the NetBSD kernel itself, so we do not need to care about.
Loading the module
Now that our device driver module is completed, we need a shell script that will be executed when the module
is successfully loaded to create the required device nodes in /dev. This shell script (or program)
is always passed three arguments: the module id (in decimal), the module type (in hexadecimal) and the character
major device number (this differs for other types of LKMs such as system call modules). So our script is pretty
simple:
if[$# -ne 3 ];then
echo"$0 should only be called from modload(8) with 3 args"exit 1
fi
First check whether all three command line arguments are present and exit with error code if not.
for i in 0 1 2 3 4 5 6 7;do
rm-f /dev/fibo$imknod /dev/fibo$i c $3$ichmod 666 /dev/fibo$idone
exit 0
And finally (re)create the required special device nodes. Now we are ready to give our module a first test run.
Compile the module and load the module with the following command (this needs to be run as superuser):
modload -e fibo_lkmentry -p fibo_post.sh fibo.o
If everything went well, the modstat(8) program
should present you output similar to this:
Type Id Off Loadaddr Size Info Rev Module Name
DEV 0 29 dca4f000 0004 dca4f260 1 fibo
Testing the module
In order to test your new kernel module, we need a small test program that does nothing more than reading a
32bit unsigned integer value from /dev/fibo0 and outputs the value to standard output. See the
sample program below:
#define DEVICE "/dev/fibo0"
intmain(intargc,char**argv){u_int32_tval;intfd,ret;if((fd=open(DEVICE,O_RDONLY))<0)err(1,"unable to open "DEVICE);while((ret=read(fd,&val,sizeof(val)))==sizeof(val))printf("%u\n",val);if(ret<0)err(2,"read("DEVICE")");close(fd);return0;}
When you run this sample test program, it will output Fibonacci numbers below 2971215074 until you interrupt
or kill the program. To unload the kernel module, you need to run the following command (as superuser):
modunload -n fibo
The complete sources for the example above, including a Makefile, are available online at:
A tar archive with the sources can be found
here.
I hope you like this small introduction to the NetBSD lkm system. If you have any questions or if you
would like to give me some feedback, feel free to contact me.
Like most other Unix(TM)-like operating systems, the K Desktop
Environment (KDE) is also available for NetBSD. KDE is a powerful graphical desktop
environment based upon Trolltech’s QT library, and is similar to other
desktop environments available for Unix(TM) workstations, such as the Common Desktop Environment
(CDE) or GNOME. It tries to combine ease of use, contemporary
functionality, and outstanding graphical design with the technological superiority of the Unix
operating system. Like NetBSD itself, KDE is also an Internet project and is licensed under the
GNU General Public License (GPL), and is therefore open source software.
Installation
It is quite easy to install the latest KDE 3 on a NetBSD system using either binary packages or
building it from pkgsrc. As I wrote this, there were no
official KDE 3 binary packages available on the ftp servers and I didn’t have the space and
bandwidth to put my packages online :-(. It would be nice, if someone could provide me with a
few 100MB of web/ftp space to put my packages on, so that everyone reading this could use these
packages.
If you are not lucky to fetch some binary packages for KDE 3, then you should fetch a recent
pkgsrc from NetBSD-current (must be a post 20020830) and start building KDE from scratch.
Therefore you should change to the directory x11/kde3 in your pkgsrc tree and
start by typing make install. NOTE: Before starting to install KDE 3, you
should remove any installed KDE package and the installed QT packages (if they’re older
than 3.0.5), and do a make clean on your pkgsrc tree, else the installation might
fail. After starting the installation process, you’ll need to wait up to 3 days, until the
installation is complete, depending on the hardware of your machine. You might want to speed
up the installation process by mounting the filesystem that contains the pkgsrc tree with the
async option; e.g. type mount -u -o async /usr/pkgsrc, but beware,
that mounting a filesystem asyncronous may have a bad effect on your data if the computer
crashes. So, you have been warned, use this option at your own risk.
Postinstallation
Now, that KDE 3 is installed properly, all you need to do, is setup your X session to start
KDE 3. Therefore edit (maybe create) the file .xsession in your home directory
and put in the following lines:
Then mark the file executable by typing chmod +x $HOME/.xsession, login as usual
with xdm and feel happy about your new and fancy KDE desktop :-).
If you’re using startx instead of xdm, you need to edit the file
.xinitrc in your home directory instead.
Using antialiased fonts
You might also want to get KDE rendering those nice fonts you saw on all those screenshots all
over the web. Therefore you’ll need to have XFree86 version
4.x installed (this is the default for NetBSD 1.6 and above). Then you need to install some nice
looking TrueType or Type1 fonts, therefore you can use the freely available webfonts provided by
Microsoft, simply install the fonts/ms-ttf
package and the fonts/ttmkfdir package.
Then goto /usr/X11R6/lib/X11/fonts/TrueType and type:
$ttmkfdir > fonts.scale
$mkfontdir
After that add the following line to your /usr/X11R6/lib/X11/XF86Config-4
in the Files section
FontPath "/usr/X11R6/lib/X11/fonts/TrueType/"
and
dir"/usr/X11R6/lib/X11/fonts/TrueType"
to your /usr/X11R6/lib/X11/XftConfig. Logout from your X session and restart your
X server by hitting CTRL-ALT-BACKSPACE at your xdm login prompt. Relogin to your
KDE desktop and enable “Use Anti-Aliasing for fonts” in the KDE control center; logout again and
relogin, and choose some nice antialiased fonts (the ones with the [Xft] in the
name) for your desktop. Enjoy! ;-).
Feedback
I hope you liked my little KDE 3 HowTo and I would be pleased to
get some good (or even bad ;-) feedback from you. If you
have any problems or questions on this topic, I’ll try to help you
getting things to work.
Today, there are various environmental sensor ICs available on modern mainboards, which are able of monitoring fan speed,
cpu and system temperature, and voltage for example. NetBSD currently supports the National Semiconductor LM78, LM79 and compatible
hardware monitors (the lm(4) device), the VIA VT82C686A hardware
monitor (the viaenv(4) device) and ACPI aware hardware
monitors (the acpi(4) subsystem) through its Environmental Systems API. These devices are not enabled
by default, you need to recompile your kernel with the
following additional lines in your kernel config file:
For the National Semiconductor LM78, LM79 and compatible monitors (you may need to adjust the I/O port settings):
lm0 at isa? port 0x290
For the VIA VT82C686A hardware monitor:
viapm* at pci? dev ? function ?
viaenv* at viapm?
For ACPI enabled monitors (thanks to Joel Carnat for the hint):
options MPACPI
acpi0 at mainbus0
acpiacad at acpi?
acpibat* at acpi?
acpibut* at acpi?
acpiec* at acpi?
acpilid* at acpi?
acpitz* at acpi?
After booting your new kernel, you can access your hardware sensors, using the envstat(8) command. For example, to display
all supported sensors, type envstat -l, and to actually read the environmental sensors, simply type
envstat. For more information about using the envstat(8)
tool, refer to the manpage.
During the last years, I used to install a lot of different operating systems, looking for the
system that fits my needs best. For example, I tried Microsoft Windows NT (3.51, 4.0 and 5.0
and had a quick look at 5.1), several Linux distributions (e.g.
SuSE Linux, RedHat Linux,
Debian GNU/Linux and Mandrake Linux),
FreeBSD and OpenBSD. We also tried to write
our own operating system (we named it “Socratix”), but soon we noticed, that it was an
impossible task for us to write a whole operating system (from scratch).
All those systems I tried have their particular advantages, but of course they all have their
native disadvantages. And not allowing me to fix these disadvantages (e.g. the Debian Policy
is such a disadvantage, I think), makes me become very frustrated of open source software (and
the philosophy behind it). So after all, I decided to give NetBSD
another try (I had installed NetBSD several times before, for testing reasons, but never
get really close to it). After reading Federico Lupi’s
excellent NetBSD Guide I fetched
the NetBSD distribution, created an iso image out of it and burned it onto a cd. Starting off
the very straight-forward installation procedure, I was very pleased with NetBSD. It allows to
me to do exactly what I want to do, nothing more, and nothing less. The base system is very
clean and includes only the main parts of the system, so soon I decided to fetch a recent
pkgsrc.tar.gz and installed some packages (I cannot live without perl ;-). The only
thing I’m still missing about pkgsrc is a tool like portupgrade; it is currently been worked on,
and e.g. pkg_hack does nearly everything portupgrade does, but you know
It doesn’t work unless it’s
right :-).
After all, I think the main reason for using NetBSD is its clean design and clean development
model. The problem with the mainstream operating systems such as GNU/Linux is that people always
want to have the newest features, and so the main goal is to get this features implemented as
soon as possible, without the need to get a clean implementation. With NetBSD, you may get this
features up to one or two years later, after somebody worked out a clean and stable
implementation and a lot of testing was done on it. And when you get it, you can be sure, it
works! So, now you know, why I prefer NetBSD, and what about you? Are you still loosing time
getting your Linux distribution to work? Tired of
fixing Debian package dependencies everytime you type apt-get dist-upgrade? So,
maybe you should give NetBSD a try, but beware of making the wrong decision: NetBSD is
no mainstream operating system, so if you always want to get the latest (unstable)
features, you’re better off not choosing NetBSD to run your computer!