Linux下PCI设备驱动开发详解 下载本文

... };

f_mode是表示的打开模式,可是读、写或者是读写。f_flags提供了更多的信息,其中有一些是与设备模块有关的,比如 O_NONBLOCK或O_NDELAY等。 这些标志是从用户空间应用程序传递给OPEN调用的,它们控制着设备在读和写方面的操作行为。所有有关资料 都可以在ASM/FCNTL.H文件里查到,f_pos是将要进行读写操作的下一个位置。

schar_open函数,下一步是设置定时器,同时schar_open最后会把打开的主、辅编号打印出来, 这两个编号是从传递给它的 inode那里提取出来的。inode它标识出磁盘或者内在中的文件,里面有属主、访问时间、长度、文件系统类型等许多有用信息。stat系统调用可以把 inode的信息提取出来。如果对一个文件执行stat命令,就可以标致诸如这个inode提供什么数据之类的许多线索(stat命令是stat系统调用 的内核外对应事物)。详细资料可以查阅linux/fs.h文件——文件注意文件系统的类型是如何在一个union类型的数据结构u里被定义的。Linux支持许多种文件系统!!!

schar_release除了将使用计数减1之外没有其它事情可做。也没有什么事情需要它做,这是因为schar设备在每次被打开时即不保有通过malloc分配到内在,也没有需要刷新的其他状态信息。

static int schar_release (struct inode *inode, struct file *file) {

MOD_DEC_USE_COUNT; MSG(\ return; }

文件操作read:从设备读出数据

schar_read函数从设备读出数据(实际上它是从内部数据队列里读到的),然后把数据传送到用户空间里去。schar用一个全局变量对数据队列 进行记录;当有数据可用的时候,变量schar_pool里的保存的是设备可以供应的数据字节数。这就是说如果在调用函数schar_read的时候变量 schar_pool是零或者一个负数,读进程就必须转入非活跃状态直到有数据可读为止。

static ssize_t schar_read(struct file *file, char *buf, size_t count, loff_t *offset) {

//if less date than requested is here, put process ot sleep while (count> schar_pool){

MSG(\

// go to sleep, but wake up on signals interruptible_sleep_on(&schar_wq);

if(signal_pending(current)){

MSG(\ //tell vfs about the signal retun -EINTR; } }

//copy the data from our buffer if(copy_to_user(buf,schar_buffer,count); return _EFAULT;

schar_pool -= count; schar.data read += count;

MSG(\

//return data written file->f_pos+= count; return count; }

current任务

在昨天schar_read函数里的好几个地方,我们都用到了current宏定义,这个宏定义代表当前运行中的进程,是一个任务结构的数据项。 也就是说,我们在schar_read里处理的那个叫做bcurrent的东西代表的就是正在读设备操作的当前进程。 任务结构有许多元素——它完整的清单可以在linux/sched.h头里看到,下面我只列出与这个例子有关的项: struct task_struct{ ....

int sigpending; .... pid_t pid; ....

sigset_t signal, blocked; ....... }

正如看到的,系统里的所有的任务其实是链接在一个双向列表里的。state给出的是进程的当前状

态,即它是正在运行、被停止、还是正在被切换。pid是该 进程的程序标识码。signal保存着与发送给该进程的一切信号有关的信息,而blocked是进程本身决定屏蔽的信号掩码。最后,sigpending 保存着与是否有一个非阻塞信号发送给了该里程有关的信息,signal_pending函数检查的就是这个变量。因此,如果signal_pending 返回的真值,我们就通知VFS让它重新开始数据传输过程。 等待队列

没有数据可读的时候,我们利用等待队列把current任务设置为休眠状态,当有新的数据拷贝到schar设备时,我们再它唤醒。这就解放了系 统,使它们可以去运行其它的进程。操作系统的进程调度器在我们唤醒任务之前是不会去考虑它的,而我们只有在使它休眠的条件得到了满足的前提下才会去唤醒 它。这使内核代码对那些访问自己的用户空间应用程序拥有了极大的控制权。 schar_read就使用了这个技术,下面看一下等待队列的数据结构定义:

struct wait_queue{ struct task_struct *task; struct wait_queue *next; }

这个结构比较明显,就不多讲了,task元素把被“催眠”的进程的有关信息保存在一个任务结构中, 而next是指向一等待队列中下一个条目的指针。如何让进程休眠呢,这就需要下面两条命令。

void interruptible_sleep_on(struct wait_queue **P)

long interruptilble_sleep_on_timeout(struct wait_queue **p, long timeout)

这两个宏命令的作用是把进程“催眠”为某个状态,但它们允许被信号唤醒。_timeout变体在内部调用了schedule_timeout,使作为其调用参数的等待队列能够让进程在时间到了的时候自动苏醒。这需要以后讲到的如何设定倒计时时间的。 void sleep_on(struct wait_queue **P)

long sleep_on_timeout(struct wait_queue **p, long timeout)

这两个宏定义的语法与上面的两个函数是完全一样的,只不过,进程的休眠状态被设置成为TASK_INTERRUPTIBLE. 可以在kernel/sched.c里查到它们 唤醒进程也是比较简单的,也不外乎下面两种方式: wake_up_interruptible(struct wait_queue **p) wake_up(struct wait_queue **P)

这两个由_wake_up延伸两个宏定义。前一个只能唤醒可中断休眠进程,而后一个可以唤醒两种休眠进程。但明确地被停止了执行(比如发送一个SIGSTOP信号)的进程不会被唤醒。 当schar设备里没有数据可提供时,它会把读数据进程催眠在自己的等待队列里。而当有一个写数据进程提供了足够多的数据,从而能够满足进程请求时,休眠进程将被 一个定时器唤醒。

interruptible_sleep_on(&schar_wq); if(signal_pending(current)) return -EINTR;

这个结构在内核的许多地方都可以看到。我们把current进程催眠了,但允许它在信号的触发下苏醒过来。在 interruptible_sleep_on成功之后,进程或者因为wake_up_interruptible调用而被唤醒,或者因为接收到一个信号 而苏醒。如果后一种情况,signal_pending先要返回“1”,我们就会以利用返回一个“-EINTR”错误的办法来激发 一个中断调用,VFS会根据这个中断重新启动读数据进程。如果我们使用sleep_on函数简单地催眠了进程,进程就会进入不可中断的休眠以等待数据;在 这种情况下,即使是一个SIGKILL信号了清能消除它。

就像我们前面介绍的那样,在定时器处理器和schar_write两个地方分别唤醒相应的读数据进程。

wake_up_interruptible(&schar_wq);

这个调用会把休眠在队列中的所有读数据进程都唤醒,这样做是否合理要看具体情况而定。 但这里我们只能满足一个读进程,所以我们是否应该唤醒所有的读进程让它们竞争数据呢? Schar设备可以通过设备的每次打开分别建立一个等待队列的办法来解决空上问题。这样做并不涉及什么新概念,只是让设备可以拥有自己的数据。 文件操作write:向设备写入数据

相对而言,schar_write就简单多了,它给schar_pool增加count个字节,再修改file->f_pos的值以反映出有 多少数据被写到设备(实际上是读 到设备的内部缓冲区里)。之所以说它比读 操作简单的多就是因为schar不需要对写到设备里来的这些数据进行处理,它只要照单全收就可以了。 除此之外,那就是唤醒读数据的进程,如下面代码:

static ssize_t schar_write(struct file *file, const char *buf) {

schar_pool +=count; schar_data_written +=count;

//if we were really writing- modify the file position to reflect the amoun of data written file->f_position += count;

if(copy_from_user(schar_buffer, buf,count)) return -EFAULT;

//wake up reading process, if any if(schar_pool>0) {

wake_up_interruptible(&schar_wq);