您的位置:首页 > 运维架构 > Linux

Linux内核开发之阻塞/非阻塞IO----等待对列

2015-01-05 20:12 162 查看
阻塞:顾名思义,就是指在执行设备操作时若不能获得资源则挂起操作,直到满足可操作的条件后再进行操作,被挂起的进程进入休眠状态,被从调度器的运行队列移

走,直到等待的条件满足。

非阻塞:就是反过来,进程在不能进行设备操作时并不挂起,它或者放弃,或者不停的查询,直到可以进行位置。

“小王,明白了没这两个基本的概念,比如就像今天的面试就是一个阻塞的问题”我补充到,“当然,是不是说非阻塞一定要不非阻塞好,答案是否定的,比如如果设备驱动不阻塞,则用户想获取设备操作就只能不断的用cpu查询(当然不可能放弃了),很显然这又会无谓的消耗CPU资源。在阻塞访问就不存在这样的问题了,不能获取资源的进程进入休眠,它将CPU资源让给了其他进程”。

“听你这么一说我, 再结合今天这架势,我是明白了,只是你刚才说的休眠什么的…那是不是如果条件满足了,再整个休眠唤醒什么的..”小王打岔道。

“不错啊, 有点我当年的风范,懂得触类旁通了,确实这样,这样,我给你两段代码,亲身感受一下吧”
阻塞地都取串口一个字符
非阻塞地都取串口一个字符
char buf;

fd = open("/dev/ttys",O_RDWR);

.. ..

res = read(fd,&buf,1); //当串口上有输入时才返回

if(res == 1)

{

printf("%c\n",buf);

}
char buf;

fd = open("/dev/ttys",O_RDWR | O_NONBLOCK);

.. ..

while( read(fd,&buf,1) !=1); //当串口上无输入也返回,所

//以要循环尝试读取串口

printf("%c\n",buf);
L

现在我们有了阻塞的方式读取,那么阻塞的进程因为没有获得资源会进入休眠状态,现在就要聊聊有关唤醒的事了。在Linux设备驱动中,可以使用等待队

列(wait queue)来实现阻塞进程的唤醒.等待队列能够用于实现内核中的异步事件通知机制。Linux提供了有关等待队列的操作:

1) wait_queue_head_t my_queue;    //定义等待队列头

2) init_waitqueue_head(&my_queue);    //初始化队列头

如果觉得上边两步来的麻烦,可以直接使用DECLARE_WAIT_QUEUE_HEAD(name)

3) DECLARE_WAITQUEUE(name,tsk);    //定义等待队列

4) void fastcall add_wait_queue(wait_queue_head_t *q, wait_queue_t *wait);

void fastcall remove_wait_queue(wait_queue_head_t *q, wait_queue_t *wait);


用于将等待队列wait添加到等待队列头指向的等待队列链表中 。

5) wait_event(queue, conditon);

wait_event_interruptible(queue, condition);//可以被信号打断

wait_event_timeout(queue, condition, timeout);

wait_event_interruptible_timeout(queue, condition, timeout);//不能被信号打断

queue:作为等待队列头的等待队列被唤醒

conditon:必须满足,否则阻塞

timeout和conditon相比,有更高优先级

6) void wake_up(wait_queue_head_t *queue);

void wake_up_interruptible(wait_queue_head_t *queue);

上述操作会唤醒以queue作为等待队列头的所有等待队列中所有属于该等待队列头的等待队列对应的进程。

7) sleep_on(wait_queue_head_t *q);

interruptible_sleep_on(wait_queue_head_t *q);

sleep_on作用是把目前进程的状态置成TASK_UNINTERRUPTIBLE,并定义一个等待队列,之后把他附属到等待队列头q,直到资源可用,q引导的等待队列被唤醒。interruptible_sleep_on作用是一样的, 只不过它把进程状态置为TASK_INTERRUPTIBLE.

这两个函数的流程是首先,定义并初始化等待队列,把进程的状态置成TASK_UNINTERRUPTIBLE或TASK_INTERRUPTIBLE,并将对待队列添加到等待队列头。

然后通过schedule(放弃CPU,调度其他进程执行。最后,当进程被其他地方唤醒,将等待队列移除等待队列头。

在Linux内核中,使用set_current_state()和__add_wait_queue()函数来实现目前进程状态的改变,直接使用current->state = TASK_UNINTERRUPTIBLE

类似的语句也是可以的。

因此我们有时也可能在许多驱动中看到,它并不调用sleep_on或interruptible_sleep_on(),而是亲自进行进程的状态改变和切换。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: