Linux系统管理:内核等待队列机制介绍2
flag = 0;wp = rp = buf;
result = register_chrdev( 54,"buf",&buf_fops );
if ( result < 0 ) {
printk( "buf: cannot get major 54 " );
return result;
}
return 0;
init_buf() 做的事就是去注册一个 character device driver.在注册一个 character device driver 之前,必须要先准备一个型别为 file_operations 结构的变量,file_operations 里包含了一些 function pointer.driver 的作者必须自己写这些 function.并将 function address 放到这个结构里。如此一来,当 user 去读取这个 device 时,kernel 才有办法去呼叫对应这个 driver 的 function.其实,简要来讲。character device driver 就是这么一个 file_operations 结构的变量。file_operations 定义在 这个档案里。它的 prototype 在 kernel 2.2.1 与以前的版本有些微的差异,这点是需要注意的地方。
register_chrdev() 看名字就大概知道是要注册 character device driver.第一个参数是此 device 的 major number.第二个是它的名字。名字你可以随便取。第三个的参数就是一个 file_operations 变量的地址。init_module() 必须要传回 0,module 才会被加载。
在 cleanup_module() 的部分,我们也是只呼叫 buf_clean() 而已。它做的事是 unregister 的动作。
if ( unregister_chrdev( 54,"buf" ) ) {
printk( "buf: unregister_chrdev error " );
}
也就是将原本记录在 device driver table 上的资料洗掉。第一个参数是 major number.第二个则是此 driver 的名称,这个名字必须要跟 register_chrdev() 中所给的名字一样才行。
现在我们来看看此 driver 所提供的 file_operations 是那些。
static struct file_operations buf_fops = {
NULL, /* lseek */
buf_read,
buf_write,
NULL, /* readdir */
NULL, /* poll */
NULL, /* ioctl */
NULL, /* mmap */
buf_open, /* open */
NULL, /* flush */
buf_release, /* release */
NULL, /* fsync */
NULL, /* fasync */
NULL, /* check_media_change */
NULL, /* revalidate */
NULL /* lock */
};
Linux系统管理:内核等待队列机制介绍2
在此,我们只打算 implement buf_read(),buf_write(),buf_open,和 buf_release()等 function 而已。当 user 对这个 device 呼叫 open() 的时候,buf_open() 会在最后被 kernel 呼叫。相同的,当呼叫 close(),read(),和 write() 时,buf_release(),buf_read(),和 buf_write() 也都会分别被呼叫。首先,我们先来看看 buf_open()。static int buf_open( struct inode *inode,struct file *filp )
MOD_INC_USE_COUNT;
return 0;
}
buf_open() 做的事很简单。就是将此 module 的 use count 加一。这是为了避免当此 module 正被使用时不会被从 kernel 移除掉。相对应的,在 buf_release() 中,我们应该要将 use count 减一。就像开启档案一样。有 open(),就应该要有对应的 close() 才行。如果 module 的 use count 在不为 0 的话,那此 module 就无法从 kernel 中移除了。
static int buf_release( struct inode *inode,struct file *filp )
{
MOD_DEC_USE_COUNT;
return 0;
}
接下来,我们要看一下buf_read()和buf_write()。
static ssize_t buf_read( struct file *filp,char *buf,size_t count,
loff_t *ppos )
{
return count;
}
static ssize_t buf_write( struct file *filp,const char *buf,
size_t count,loff_t *ppos )
{
return count;
}
在此,我们都只是回传 user 要求读取或写入的字符数目而已。在此,我要说明一下这些参数的意义。filp 是一个 file 结构的 pointer.也就是指我们在 /dev 下所产生的 buf 档案的 file 结构。当我们呼叫 read() 或 write() 时,必须要给一个 buffer 以及要读写的长度。Buf 指的就是这个 buffer,而 count 指的就是长度。至于 ppos 是表示目前这个档案的 offset 在那里。这个值对普通档案是有用的。也就是跟 lseek() 有关系。由于在这里是一个 drvice.所以 ppos 在此并不会用到。有一点要小心的是,上面参数 buf 是一个地址,而且还是一个 user space 的地址,当 kernel 呼叫 buf_read() 时,程序在位于 kernel space.所以你不能直接读写资料到 buf 里。必须先切换 FS 这个 register 才行。
Linux系统管理:内核等待队列机制介绍2
MakefileP = buf
OBJ = buf.o
INCLUDE = -I/usr/src/linux/include/linux
CFLAGS = -D__KERNEL__ -DMODVERSIONS -DEXPORT_SYMTAB -O $(INCLUDE)
-include /usr/src/linux/include/linux/modversions.h
CC = gcc
$(P): $(OBJ)
ld -r $(OBJ) -o $(P)。o
.c.o:
$(CC) -c $(CFLAGS) $
Linux系统管理:内核等待队列机制介绍2
if ( ( rp != wp ) && ( count > 0 ) )goto repeate_reading;
flag = 0;
wake_up_interruptible( &write_wq );
return nRead;
}
在前头我有提到,buf 的地址是属于 user space 的。在 kernel space 中,你不能像普通写到 buffer 里一样直接将资料写到 buf 里,或直接从 buf 里读资料。Linux 里使用 FS 这个 register 来当作 kernel space 和 user space 的切换。所以,如果你想手动的话,可以这样做:
mm_segment_t fs;
fs = get_fs();
set_fs( USER_DS );
write_data_to_buf( buf );
set_fs( fs );
也就是先切换到 user space,再写资料到 buf 里。之后记得要切换回来 kernel space.这种自己动手的方法比较麻烦,所以 Linux 提供了几个 function,可以让我们直接在不同的 space 之间做资料的搬移。诚如各位所见,copy_to_user() 就是其中一个。
copy_to_user( to,from,n );
copy_from_user( to,from,n );
顾名思义,copy_to_user() 就是将资料 copy 到 user space 的 buffer 里,也就是从 to 写到 from,n 为要 copy 的 byte 数。相同的,copy_from_user() 就是将资料从 user space 的 from copy 到位于 kernel 的 to 里,长度是 n bytes.在以前的 kernel 里,这两个 function 的前身是 memcpy_tofs() 和 memcpy_fromfs(),不知道为什么到了 kernel 2.2.1之后,名字就被改掉了。至于它们的程序代码有没有更改就不太清楚了。至于到那一版才改的。我没有仔细去查,只知道在 2.0.36 时还没改,到了 2.2.1 就改了。这两个 function 是 macro,都定义在 里。要使用前记得先 include 进来。
相信 buf_read() 的程序代码应当不难了解才对。不知道各位有没有看到,在buf_read() 的后面有一行的程序,就是
wake_up_interruptible( &write_wq );
write_wq 是我们用来放那些想要写资料到 buffer,但 buffer 已满的 process.这一行的程序会将挂在此 queue 上的 process 叫醒。当 queue 是空的时,也就是当 write_wq 为 NULL 时,wake_up_interruptible() 并不会造成任何的错误。接下来,我们来看看更改后的 buf_write()。
static ssize_t buf_write( struct file *filp,const char *buf,size_t count,loff_t *ppos )
{
int num,nWrite;
nWrite = 0;
while ( ( wp == rp ) && flag ) {
interruptible_sleep_on( &write_wq );
}
repeate_writing:
if ( rp > wp ) {
num = min( count,( int ) ( rp - wp ) );
}
else {
num = min( count,( int ) ( buffer + BUF_LEN - wp ) );
}
copy_from_user( wp,buf,num );
wp += num;
count -= num;
nWrite += num;
if ( wp == ( buffer + BUF_LEN ) ) {
wp = buffer;
Linux系统管理:内核等待队列机制介绍2
}if ( ( wp != rp ) && ( count > 0 ) ) {
goto repeate_writing;
}
flag = 1;
return nWrite;
}
我们把 process 丢到 write_wq 的动作放在 buf_write() 里。当 buffer 已满时,就直接将 process 丢到 write_wq 里。
while ( ( wp == rp ) && flag ) {
interruptible_sleep_on( &write_wq );
}
好了。现在程序已经做了一些修改。再重新 make 一次,利用 insmod 将 buf.o 载到 kernel 里就行了。接着,我们就来试验一下是不是真正做到 block IO.
# cd /dev
# ls -l ~/WWW-HOWTO
-rw-r--r-- 1 root root 23910 Apr 14 16:50 /root/WWW-HOWTO
# cat ~/WWW-HOWTO > buf
执行到这里,应该会被 block 住。现在,我们再开一个 shell 出来。
# cd /dev
# cat buf
…( contents of WWW-HOWTO ) …skip …
此时,WWW-HOWTO 的内容就会出现了。而且之前 block 住的 shell 也已经回来了。最后,试验结束,可以下
# rmmod buf
将 buf 这个 module 从 kernel 中移除。以上跟各位介绍的就是 wait_queue 的使用。希望能对各位有所助益。
我想对某些人来讲,会使用一个东西就够了。然而对某些人来讲,可能也很希望知道这项东西是如何做出来的。至少我就是这种人。在下面,我将为各位介绍 wait_queue 的 implementation.如果对其 implementation 没兴趣,以下这一段就可以略过不用看了。
页:
[1]