什么情况下进程被阻塞?什么情况下进程被唤醒心中的巨人下载

第5章 字符设备驱动程序的扩展操作在关于字符设备驱动程序的那一章中,我们构建了一..
扫扫二维码,随身浏览文档
手机或平板扫扫即可继续访问
字符设备驱动程序的扩展操作
举报该文档为侵权文档。
举报该文档含有违规或不良信息。
反馈该文档无法正常浏览。
举报该文档为重复文档。
推荐理由:
将文档分享至:
分享完整地址
文档地址:
粘贴到BBS或博客
flash地址:
支持嵌入FLASH地址的网站使用
html代码:
&embed src='/DocinViewer-4.swf' width='100%' height='600' type=application/x-shockwave-flash ALLOWFULLSCREEN='true' ALLOWSCRIPTACCESS='always'&&/embed&
450px*300px480px*400px650px*490px
支持嵌入HTML代码的网站使用
您的内容已经提交成功
您所提交的内容需要审核后才能发布,请您等待!
3秒自动关闭窗口如何唤醒socket被阻塞的函数-C++-第七城市
如何唤醒socket被阻塞的函数
& & 最近项目遇到一个问题,程序退出的时候资源没有正常释放。经过调试发现,原来是网络线程一直阻塞,导致一些必要的资源没有被释放,写了几个简单的测试程序调试了一下才明白,原来在Linux下直接close socket的文件描述符,并不会使程序中调用的一些阻塞式的socket函数(比如 read、recvfrom 等)退出阻塞,从而导致无法正常释放资源。简化示例如下。& & 下面是一个简化的UDP服务程序,首先创建socket对象,然后开启服务线程,将客户端发送过来的数据包回发给客户端。当用户在shell中敲入两次回车后,程序退出。我们来观察一下程序退出后,socket服务线程在怎样的情况下可以正常退出。#include&&stdio.h&&&#include&&sys/types.h&&&#include&&sys/socket.h&&&#include&&linux/in.h&&&#include&&string.h&&&#include&&pthread.h&&&&&#define&SERVER_PORT&8888&&#define&BUFFER_LEN&&256&&&&int&g_Exit&=&0;&&&&void&*service(&void*&arg&)&&{&&&&&&char&buff[BUFFER_LEN];&&&&&&struct&sockaddr&clientA&&&&&&int&socklen&=&sizeof(clientAddr);&&&&&&int&&&&&&&int&socketfd&=&*((int&*)arg);&&&&&&&&printf(&OK,&Enter&Service!/n&);&&&&&&&&while(!g_Exit)&&&&&&{&&&&&&&&&&recvbytes&=&recvfrom(socketfd,buff,BUFFER_LEN,0,&clientAddr,&socklen);&&&&&&&&&&&&sendto(socketfd,buff,recvbytes,0,&clientAddr,socklen);&&&&&&}&&&&&&&&printf(&OK,&Service&Thread&Exit!/n&);&&&&&&&&pthread_exit(NULL);;&&}&&&&int&mn(&int&argc,char&*&argv[]&)&&{&&&&&&int&&&&&&&void&*&&&&&&struct&sockaddr_in&serverA&&&&&&pthread_t&&&&&&&pthread_attr_t&&&&&&&&&fd&=&socket(AF_INET,SOCK_DGRAM,0);&&&&&&&&memset(&serverAddr,0,sizeof(serverAddr));&&&&&&&&serverAddr.sin_family&=&AF_INET;&&&&&&serverAddr.sin_addr.s_addr&=&htonl(INADDR_ANY);&&&&&&serverAddr.sin_port&=&htons(SERVER_PORT);&&&&&&&&bind(fd,(struct&sockaddr&*)&serverAddr,sizeof(serverAddr));&&&&&&&//&create&service&thread&&&&&&pthread_attr_init(&attr);&&&&&&pthread_attr_setdetachstate(&attr,&PTHREAD_CREATE_JOINABLE);&&&&&&if(&pthread_create(&thr,&attr,service,(void&*)&fd&)&)&&&&&&{&&&&&&&&&&printf(&pthread_create&fl!/n&);&&&&&&&&&&return&-1;&&&&&&}&&&&&&//&Free&attribute&&&&&&pthread_attr_destroy(&attr);&&&&&&&&//&wt&user&control&exit&&&&&getchar();&&&&&&getchar();&&&&&&&&g_Exit&=&1;&&&&&&&&&&&&printf(&OK,&Wting&For&Thread&Exit...!/n&);&&&&&&
close(fd);&&&&&&// wt for thread exit
&&pthread_join(thr, &status);&printf(&OK, Exit Mn Process !/n&);&&&&&return&0;&&}&&& & 上述程序,当用户敲两次回车后,显示结果如下:& &&650) this.width=650;" src="/d/file/p//baec08cfdf800b2d8ca6b6.jpg" border="0" alt="" />& & 可以看到,没有打出主进程和服务线程的退出信息,无论是主进程还是服务线程都没有正常退出,由此可见,直接close socket句柄,并不能使 recvfrom 函数退出阻塞。& & 那么,如果把 pthread_join 换成 pthread_cancel 呢?结果是一样的,虽然主进程退出了,但依然无法让 service 线程正常退出。那么,该如何才能正常退出 recvfrom 的阻塞呢?& & 网上搜了一下,可以考虑使用 shutdown 函数。//shutdown函数原型为:&&#include&&sys/socket.h&&&int&shutdown(int&s,&int&how);&&&&//shutdown()&可以对套接字的关闭进行更细致的控制,它允许对套接字进行单向关闭或全部禁止。&&//参数&s&为待关闭的套接字描述符。&&//参数&how&指定了关闭方式,具体取值如下:&&//SHUT_RD&:&将连接上的读通道关闭,此后进程将不能再接收到任何数据,接收缓冲区中还未被读取的数据也将被丢弃,但仍然可以在该套接字上发送数据。&&//SHUT_WR&:&将连接上的写通道关闭,此后进程将不能再发送任何数据,发送缓冲区中还未被发送的数据也将被丢弃,但仍然可以在该套接字上接收数据。&&//SHUT_RDWR&:&读、写通道都将被关闭。&&//执行成功返回&0,出错则返回&-1,错误代码存入&errno&中。&&& & 可以测试一下,我们在上述代码的pthread_join前面加上一句:shutdown(fd,SHUT_RDWR); 然后再编译调试,结果如下:&& &&650) this.width=650;" src="/d/file/p//26eff50fb93bf174a84b.jpg" border="0" alt="" />& & 可以看到,Service服务线程已经正常退出了。进一步测试,如果只是shutdown写通道或者只shutdown读通道呢?& & 经过测试可以发现,如果只关闭写通道 shutdown(fd,SHUT_WR); 服务线程依然无法正常退出,而如果只关闭读通道 shutdown(fd,SHUT_RD),则服务线程正常退出了。分析如下:因为recvfrom在fd的读通道等待列表中,因此必须关闭读通道时才能将recvfrom阻塞唤醒。& &&那么,为啥shutdown就可以使得recvfrom退出阻塞,而close却不能呢?& & 我的理解如下:shutdown破坏了socket连接的读写通道,导致读写阻塞的socket函数被唤醒,而close函数只是做了关闭连接释放socket资源的操作,却并没有进行读写通道的清理工作,从而无法成功唤醒读写函数的阻塞。(期待高手给出更深层次的解释)& & 进一步,那么,解决这一问题,还有其他的什么办法没有?& & 下面我简单地罗列一下网上搜到的可行的一些方法,以后有时间再深入研究:& & 1. &设置socket发送/接收超时& & 2. &使用非阻塞方式,异步socket模型& & 3. &其他方式,欢迎大家补充。& & 文章就写到这里了,欢迎大家来信进一步交流lujun.&本文出自 “对影成三人” 博客,请务必保留此出处http://ticktick./536高级字符驱动学习--阻塞型I/0 - Linux设备驱动程序学习笔记_Linux编程_Linux公社-Linux系统门户网站
你好,游客
Linux设备驱动程序学习笔记
高级字符驱动学习--阻塞型I/0
来源:Linux社区&
作者:en_wang
提出问题:若驱动程序无法立即满足请求,该如何响应? 比如:当数据不可用时调用read,或是在缓冲区已满时,调用write
解决问题:驱动程序应该(默认)该阻塞进程,将其置入休眠状态直到请求可继续。
当一个进程被置入休眠时,它会被标记为一种特殊状态并从调度器运行队列中移走,直到某些情况下修改了这个状态,才能运行该进程。
安全进入休眠两原则:
1.永远不要在原子上下文中进入休眠。(原子上下文:在执行多个步骤时,不能有任何的并发访问。这意味着,驱动程序不能再拥有自旋锁,seqlock,或是RCU锁时,休眠)
2.对唤醒之后的状态不能做任何假定,因此必须检查以确保我们等待的条件真正为真
临界区& vs& 原子上下文
原子上下本:一般说来,具体指在中断,软中断,或是拥有自旋锁的时候。
临界区:每次只允许一个进程进入临界区,进入后不允许其它进程访问。
other question:
要休眠进程,必须有一个前提:有人能唤醒进程,而起这个人必须知道在哪儿能唤醒进程,这里,就引入了“等待队列”这个概念。
等待队列:就是一个进程链表(我的理解:是一个休眠进程链表),其中包含了等待某个特定事件的所有进程。
等待队列头:wait_queue_head_t,定义在&linux/wait.h&
定义方法:静态& DECLARE_QUEUE_HEAD(name)
&&&&&&&&&&&&&&&&&&&&& 动态& wait_queue_head_t& my_
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& &init_waitqueue_head(&my_queue);
struct __wait_queue_head {&&&&spinlock_&&&&struct list_head task_};typedef struct __wait_queue_head wait_queue_head_t;
linux最简单的休眠方式是wait_event(queue,condition)及其变种,在实现休眠的同时,它也检查进程等待的条件。四种wait_event形式如下:
wait_event(queue,condition);/*不可中断休眠,不推荐*/
wait_event_interruptible(queue,condition);/*推荐,返回非零值意味着休眠被中断,且驱动应返回-ERESTARTSYS*/
wait_event_timeout(queue,condition,timeout);
wait_event_interruptible_timeout(queue,conditon,timeout);/*有限的时间的休眠,若超时,则不管条件为何值返回0*/
唤醒休眠进程的函数:wake_up
void wake_up(wait_queue_head_t& *queue);
void wake_up_interruptible(wait_queue_head& *queue);
惯例:用wake_up唤醒wait_event,用wake_up_interruptible唤醒wait_event_interruptible
休眠与唤醒 实例分析:
本例实现效果为:任何从该设备上读取的进程均被置于休眠。只要某个进程向给设备写入,所有休眠的进程就会被唤醒。
static DECLARE_WAIT_QUEUE_HEAD(wq);
static int flag =0;
ssize_t sleepy_read(struct file *filp,char __user *buf,size_t count,loff_t *pos)
pirntk(KERN_DEBUG "process %i (%s) going to sleep\n",current-&pid,current-&comm);
wait_event_interruptible(wq,flag!=0);
printk(KERN_DEBUG "awoken %i (%s) \n",current-&pid,current-&comm);
ssize_t sleepy_write(struct file *filp,const char __user *buf,size_t count,loff_t *pos)
printk(KERN_DEBUG "process %i (%s) awakening the readers ...\n",current-&pid,current-&comm);
wake_up_interruptible(&wq);
& /*成功并避免重试*/
阻塞与非阻塞类操作
小知识点:
操作系统中睡眠、阻塞、挂起的区别形象解释
操作系统中睡眠、阻塞、挂起的区别形象解释首先这些术语都是对于线程来说的。对线程的控制就好比你控制了一个雇工为你干活。你对雇工的控制是通过编程来实现的。
挂起线程的意思就是你对主动对雇工说:“你睡觉去吧,用着你的时候我主动去叫你,然后接着干活”。
使线程睡眠的意思就是你主动对雇工说:“你睡觉去吧,某时某刻过来报到,然后接着干活”。
线程阻塞的意思就是,你突然发现,你的雇工不知道在什么时候没经过你允许,自己睡觉呢,但是你不能怪雇工,肯定你这个雇主没注意,本来你让雇工扫地,结果扫帚被偷了或被邻居家借去了,你又没让雇工继续干别的活,他就只好睡觉了。至于扫帚回来后,雇工会不会知道,会不会继续干活,你不用担心,雇工一旦发现扫帚回来了,他就会自己去干活的。因为雇工受过良好的培训。这个培训机构就是操作系统。
全功能的 read 和 write 方法涉及到进程可以决定是进行非阻塞 I/O还是阻塞 I/O操作。明确的非阻塞 I/O 由 filp-&f_flags 中的 O_NONBLOCK 标志来指示(定义再&linux/fcntl.h& ,被&linux/fs.h&自动包含)。浏览源码,会发现O_NONBLOCK 的另一个名字:O_NDELAY ,这是为了兼容 System V 代码。O_NONBLOCK 标志缺省地被清除,因为等待数据的进程的正常行为只是睡眠.
其实不一定只有read 和 write 方法有阻塞操作,open也可以有阻塞操作。
1.如果指定了O_NONBLOCK标志,read和write的行为就会有所不同。如果在数据没有就绪时调用read或是在缓冲区没有空间时调用write,则该调用简单的返回-EAGAIN。
2.非阻塞型操作会立即返回,使得应用程序可以查询数据。在处理非阻塞型文件时,应用程序调用stdio函数必须非常小心,因为很容易就把一个非阻塞返回误认为EOF,所以必须始终检查errno。
3.有些驱动程序还为O_NONBLOCK实现了特殊的语义。例如,在磁带还没有插入时打开一个磁带设备通常会阻塞,如果磁带驱动程序使用O_NONBLOCK打开的,则不管磁带在不在,open都会立即成功返回。
4.只有read,write,open文件操作受非阻塞标志的影响。
&read负责管理阻塞型和非阻塞型输入,如下所示:
static ssize_t scull_p_read (struct file *filp,char __user *buf,size_t count,loff_t *f_pos)
struct scull_pipe *dev = filp-&private_
if(down_interruptible(&dev))
&&&& return -ERRESTARTSYS;
while(dev-&rp == dev-&wp){/*无数据可读*/
up(&dev-&sem); /*释放锁*/
if(filp-&f_flags & O_NONBLOCK)
&&&&&& return&-EAGAIN;
PDEBUG("\“ %s ”\& reading: going to sleep\n",current-&comm);
if(wait_event_interruptible(dev-inq,(dev-&rp!=dev-wp)))
&&&&&&& return -ERESTARTSYS;
/*数据已就绪,返回*/
if(dev-&wp & dev-&rp)
&&& count=min(count,(size_t) (dev-&wp - dev-&rp));
else& /*写入指针回卷,返回数据直到dev-&end*/
&&& count = min(count,(size_t)(dev-&end - dev-&rp))
if(copy_to_user(buf,dev-&rp,count)){
up(&dev-&sem);
return -EFAULT;
dev-&rp +=
if(dev-&rp == dev-&end)
&&& dev-&rp = dev-&/*回卷*/
up(&dev-&sem);
/*最后,唤醒所有写入者并返回*/
wake_up_interruptible(&dev-&outq);
PDEBUG("\%s\" did read %li bytes \n ",current-&comm,(long)count");
&代码分析:
while循环在拥有设备信号量时测试缓冲区。如果其中有数据,则可以立即将数据返回给用户而不需要休眠,这样,整个循环体就被跳过了。相反,如果缓冲区为空,则必须休眠。但在休眠之前必须释放设备信号量,因为如果在拥有该信号量时休眠,任何写入者都没有机会来唤醒。在释放信号量之后,快速检测用户请求的是否是非阻塞I/O,如果是,则返回,否则调用wait_event_interruptible。
高级休眠:
进程休眠步骤:
1.分配并初始化一个wait_queue_t结构,然后将其加入到对应的等待队列
2.设置进程的状态,将其标记为休眠在 &linux/sched.h& 中定义有几个任务状态:TASK_RUNNING意思是进程可运行。有 2 个状态指示一个进程是在睡眠:TASK_INTERRUPTIBLE 和 TASK_UNTINTERRUPTIBLE。
2.6 内核的驱动代码通常不需要直接操作进程状态。但如果需要这样做使用的代码是:
void set_current_state(int new_state);
在老的代码中, 你常常见到如此的东西:current-&state = TASK_INTERRUPTIBLE; 但是象这样直接改变 current 是不推荐的,当数据结构改变时这样的代码将会失效。通过改变 current 状态,只改变了调度器对待进程的方式,但进程还未让出处理器。
3.最后一步:释放处理器。但之前我们必须首先检查休眠等待的条件。如果不做这个检查,可能会引入竞态:如果在忙于上面的这个过程时有其他的线程刚刚试图唤醒你,你可能错过唤醒且长时间休眠。因此典型的代码下:
if (!condition)&&&&schedule();
如果代码只是从 schedule 返回,则进程处于TASK_RUNNING 状态。 如果不需睡眠而跳过对 schedule 的调用,必须将任务状态重置为 TASK_RUNNING,还必要从等待队列中去除这个进程,否则它可能被多次唤醒
手工休眠:
1.建立并初始化一个等待队列入口。
DEFINE_WAIT(my_wait);
wait_queue_t my_
init_wait(&my_wait);
2.将我们的等待队列入口添加到队列中,并设置进程的状态。
void prepare_to_wait(wait_queue_head_t *queue,
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& wait_queue_t *wait,
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& int state);
queue和wait分别是等待队列头和进城入口,state是进程的新状态,它应该是TASK_INTERRUPTIBLE(可中断休眠)或者TASK_UNINTERRUPTIBLE(不可中断休眠)
3.在调用prepaer_to_wait之后,进程即可调用schedule,当然在这之前,应确保仍有必有等待。一旦schedule返回,就到了清理时间了。
void finesh_wait(wait_queue_head_t *queue,wait_queue_t *wait);
当一个进程调用 wake_up 在等待队列上,所有的在这个队列上等待的进程被置为可运行的。 这在许多情况下是正确的做法。但有时,可能只有一个被唤醒的进程将成功获得需要的资源,而其余的将再次休眠。这时如果等待队列中的进程数目大,这可能严重降低系统性能。为此,内核开发者增加了一个“独占等待”选项。它与一个正常的睡眠有 2 个重要的不同:
1.当等待队列入口设置了 WQ_FLAG_EXCLUSEVE 标志,它被添加到等待队列的尾部;否则,添加到头部。
2.当 wake_up 被在一个等待队列上调用, 它在唤醒第一个有 WQ_FLAG_EXCLUSIVE 标志的进程后停止唤醒.但内核仍然每次唤醒所有的非独占等待。
采用独占等待要满足 2 个条件:
(1)希望对资源进行有效竞争;
(2)当资源可用时,唤醒一个进程就足够来完全消耗资源。
使一个进程进入独占等待,可调用:
void prepare_to_wait_exclusive(wait_queue_head_t *queue, wait_queue_t *wait, int state);
&注意:无法使用 wait_event 和它的变体来进行独占等待.
唤醒的相关函数
很少会需要调用wake_up_interruptible 之外的唤醒函数,但为完整起见,这里是整个集合:
wake_up(wait_queue_head_t*queue);wake_up_interruptible(wait_queue_head_t *queue);/*wake_up 唤醒队列中的每个非独占等待进程和一个独占等待进程。wake_up_interruptible 同样, 除了它跳过处于不可中断休眠的进程。它们在返回之前, 使一个或多个进程被唤醒、被调度(如果它们被从一个原子上下文调用, 这就不会发生).*/
wake_up_nr(wait_queue_head_t *queue, int nr);wake_up_interruptible_nr(wait_queue_head_t *queue, int nr);/*这些函数类似 wake_up, 除了它们能够唤醒多达 nr 个独占等待者, 而不只是一个. 注意传递 0 被解释为请求所有的互斥等待者都被唤醒*/
wake_up_all(wait_queue_head_t*queue);wake_up_interruptible_all(wait_queue_head_t *queue);/*这种 wake_up 唤醒所有的进程, 不管它们是否进行独占等待(可中断的类型仍然跳过在做不可中断等待的进程)*/
wake_up_interruptible_sync(wait_queue_head_t *queue);/*一个被唤醒的进程可能抢占当前进程, 并且在 wake_up 返回之前被调度到处理器。 但是, 如果你需要不要被调度出处理器时,可以使用 wake_up_interruptible 的"同步"变体. 这个函数最常用在调用者首先要完成剩下的少量工作,且不希望被调度出处理器时。*/
poll and select
当应用程序需要进行对多文件读写时,若某个文件没有准备好,则系统会处于读写阻塞的状态下,并影响了其它文件的读写。为了避免这种情况的发生,则必须使用多输入输出流又不想阻塞在他们任何一个上的应用程序常将非阻塞的I/O和poll(system V),select(BSD Unix),epoll(linux2.5.45开始)系统调用配合使用。当poll函数返回时,会给出一个文件是否可读写的标志,应用程序根据不同的标识读写相应的文件,实现非阻塞的读写。这些系统调用功能相同:允许进程来决定它是否可读或写一个或多个文件而不阻塞。这些调用也可阻塞进程直到任何一个给定集合的文件描述符可用来读或写。这些调用都需要来自设备驱动中的poll方法的支持。poll返回不同的标识,告诉主进程文件是否可以读写,其原型:
&linux/poll.h&
unsigned int (*poll) (struct file *filp,poll_table *wait);
实现这个设备方法分两步:
1.在一个或多个可指示查询状态变化的等待队列上调用poll_wait,如果没有文件描述符可用来执行I/O,内核使这个进程在等待队列上等待所有的传递给系统调用的文件描述符,驱动通过调用函数poll_wait增加一个队列到poll_wait结构,原型:
void poll_wait(struct file *,wait_queue_head_t *,poll_table*);
2.返回一个位掩码:描述可能不必阻塞就立刻进行的操作,几个标志(通过&linux/poll.h&定义)用来指示可能的操作:
&如果设备无阻塞的读,就返回该值
&POLLRDNORM
&通常的数据已经准备好,可以读了,就返回该值。通常的做法是会返回(POLLLIN|POLLRDNORA)
&POLLRDBAND
&如果可以从设备读出带外数据,就返回该值,它只可在linux内核的某些网络代码中使用,通常不用在设备驱动程序中
&如果可以无阻塞的读取高优先级(带外)数据,就返回该值,返回该值会导致select报告文件发生异常,以为select八带外数据当作异常处理
&当读设备的进程到达文件尾时,驱动程序必须返回该值,依照select的功能描述,调用select的进程被告知进程时可读的。
&如果设备发生错误,就返回该值。
&如果设备可以无阻塞地些,就返回该值
&POLLWRNORM
&设备已经准备好,可以写了,就返回该值。通常地做法是(POLLOUT|POLLNORM)
&POLLWRBAND
&于POLLRDBAND类似
使用举例:
static unsigned int scull_p_poll(struct file *filp, poll_table *wait){&&&&&&&&struct scull_pipe *dev = filp-&private_&&&&&&&&unsigned int mask = 0;&&&&&&&&/*&&&&&&&&* Th it is considered full&&&&&&&&* if "wp" is right behind "rp" and empty if the&&&&&&&&* two are equal. &&&&&&&&*/&&&&&&&&down(&dev-&sem);&&&&&&&&poll_wait(filp, &dev-&inq, wait);&&&&&&&&poll_wait(filp, &dev-&outq, wait);&&&&&&&&if (dev-&rp != dev-&wp)&&&&&&&&&&&&&&&&mask |= POLLIN | POLLRDNORM; /* readable */&&&&&&&&if (spacefree(dev))&&&&&&&&&&&&&&&&mask |= POLLOUT | POLLWRNORM; /* writable */&&&&&&&&up(&dev-&sem);&&&&&&&&}
与read与write的交互
正确实现poll调用的规则:
从设备读取数据:
(1)如果在输入缓冲中有数据,read 调用应当立刻返回,即便数据少于应用程序要求的,并确保其他的数据会很快到达。 如果方便,可一直返回小于请求的数据,但至少返回一个字节。在这个情况下,poll 应当返回 POLLIN|POLLRDNORM。
(2)如果在输入缓冲中无数据,read默认必须阻塞直到有一个字节。若O_NONBLOCK 被置位,read 立刻返回 -EAGIN 。在这个情况下,poll 必须报告这个设备是不可读(清零POLLIN|POLLRDNORM)的直到至少一个字节到达。
(3)若处于文件尾,不管是否阻塞,read 应当立刻返回0,且poll 应该返回POLLHUP。
向设备写数据
(1)若输出缓冲有空间,write 应立即返回。它可接受小于调用所请求的数据,但至少必须接受一个字节。在这个情况下,poll应返回 POLLOUT|POLLWRNORM。
(2)若输出缓冲是满的,write默认阻塞直到一些空间被释放。若 O_NOBLOCK 被设置,write 立刻返回一个 -EAGAIN。在这些情况下, poll 应当报告文件是不可写的(清零POLLOUT|POLLWRNORM). 若设备不能接受任何多余数据, 不管是否设置了 O_NONBLOCK,write 应返回 -ENOSPC("设备上没有空间")。
(3)永远不要让write在返回前等待数据的传输结束,即使O_NONBLOCK 被清除。若程序想保证它加入到输出缓冲中的数据被真正传送, 驱动必须提供一个 fsync 方法。
永远不要让write调用在返回前等待数据传输的结束,即使O_NONBLOCK标志被清除。这是因为很多应用程序使用select来检查write是否会阻塞。如果报告设备可以写入,调用就不能被阻塞。如果使用设备的程序需要保证输出缓冲区中的数据确实已经被传送出去,驱动程序就必须提供一个fsync方法。
刷新待处理输出
若一些应用程序需要确保数据被发送到设备,就必须实现fsync 方法。对 fsync 的调用只在设备被完全刷新时(即输出缓冲为空)才返回,不管 O_NONBLOCK 是否被设置,即便这需要一些时间。其原型是:
int(*fsync)(structfile*file,struct dentry*dentry,int datasync);
底层的数据结构
通过使用异步通知,应用程序可以在数据可用时收到一个信号,而无需不停地轮询。
启用步骤:
(1)它们指定一个进程作为文件的拥有者:使用 fcntl 系统调用发出 F_SETOWN 命令,这个拥有者进程的 ID 被保存在 filp-&f_owner。目的:让内核知道信号到达时该通知哪个进程。
(2)使用 fcntl 系统调用,通过 F_SETFL 命令设置 FASYNC 标志。
内核操作过程
1.F_SETOWN被调用时filp-&f_owner被赋值。
2. 当 F_SETFL 被执行来打开 FASYNC, 驱动的 fasync 方法被调用.这个标志在文件被打开时缺省地被清除。
3. 当数据到达时,所有的注册异步通知的进程都会被发送一个 SIGIO 信号。
Linux 提供的通用方法是基于一个数据结构和两个函数,定义在&linux/fs.h&。
数据结构:
struct fasync_struct{&&&&int&&&&magic;&&&&int&&&&fa_fd;&&&&struct&&&&fasync_struct&&&&*fa_next;/* singly linked list */&&&&struct&&&&file &&&&&&&&*fa_file;};
&驱动调用的两个函数的原型:
int fasync_helper(int fd,structfile*filp,int mode, struct fasync_struct**fa);void kill_fasync(struct fasync_struct**fa,int sig, int band);
当一个打开的文件的FASYNC标志被修改时,调用fasync_helper 来从相关的进程列表中添加或去除文件。除了最后一个参数, 其他所有参数都时被提供给 fasync 方法的相同参数并被直接传递。 当数据到达时,kill_fasync 被用来通知相关的进程,它的参数是被传递的信号(常常是 SIGIO)和 band(几乎都是 POLL_IN)。
这是 scullpipe 实现 fasync 方法的:
staticint scull_p_fasync(int fd,struct file*filp,int mode){&struct scull_pipe *dev = filp-&private_data;&return fasync_helper(fd, filp, mode,&dev-&async_queue);}
当数据到达, 下面的语句必须被执行来通知异步读者. 因为对 sucllpipe 读者的新数据通过一个发出 write 的进程被产生, 这个语句出现在 scullpipe 的 write 方法中:if (dev-&async_queue) &kill_fasync(&dev-&async_queue, SIGIO, POLL_IN); /*&注意, 一些设备也针对设备可写而实现了异步通知,在这个情况,kill_fasnyc 必须以 POLL_OUT 模式调用.*/
当文件被关闭时必须调用fasync 方法,来从活动的异步读取进程列表中删除该文件。尽管这个调用仅当 filp-&f_flags 被设置为 FASYNC 时才需要,但不管什么情况,调用这个函数不会有问题,并且是普遍的实现方法。 以下是 scullpipe 的 release 方法的一部分:/* remove this filp from the asynchronously notified filp's */ scull_p_fasync(-1, filp, 0);
异步通知使用的数据结构和 struct wait_queue 几乎相同,因为他们都涉及等待事件。区别异步通知用 struct file 替代 struct task_struct. 队列中的 file 用获取 f_owner, 一边给进程发送信号。6
【内容导航】
相关资讯 & & &
& (11/20/:23)
& (06/21/:31)
& (12/17/:12)
& (06/21/:37)
& (06/17/:43)
图片资讯 & & &
   同意评论声明
   发表
尊重网上道德,遵守中华人民共和国的各项有关法律法规
承担一切因您的行为而直接或间接导致的民事或刑事法律责任
本站管理人员有权保留或删除其管辖留言中的任意内容
本站有权在网站内转载或引用您的评论
参与本评论即表明您已经阅读并接受上述条款

我要回帖

更多关于 下腔静脉阻塞 的文章

 

随机推荐