• Linux进程阻塞的相关知识


    1.如果驱动程序无法立即满足要求,该如何响应?

    当数据不可用时,用户可能调用read;或者进程试图写入数据,但因为输出缓冲区已满,设备还未准备好接受数据。调用进程通常不会关心这类问题,程序员只会简单调用read和write,然后等待必要的工作结束后返回调用。因此,在这种情况下,我们的驱动程序应该(默认)阻塞该进程,将其置入休眠状态直到请求可继续。

    2.“休眠(sleep)”对进程来讲意味着什么?

    当一个进程被置入休眠时,它会被标记为一种特殊状态并从调度器的运行队列中移走。直到某些情况下修改了这个状态,进程才会在任意CPU上调度,也即运行该进程。休眠中的进程会被搁置在一边,等待将来的某个事件发生。对Linux设备驱动程序来讲,让一个进程进入休眠状态很容易。但是,为了将进程以一种安全的方式进入休眠,我们需要牢记两天规则:

    1)        永远不要在原子上下文中进入休眠

    2)        当我们被唤醒是,我们永远无法知道休眠了多长时间,或者休眠期间都发生了什么事。

    等待队列:就是一个进程链表,其中包含了等待某个特定事件的所有进程。在Linux中,一个等待队列通过一个“等待队列头”来管理,等待队列头是一个类型为wait_queue_head_t的结构体。

    DECLARE_WAIT_QUEUE_HEAD(name);//静态定义并初始化一个等待队列头

    动态方法:

    Wait_queue_head_t  my_queue;

    Init_waitqueue_head(&my_queue);

    3.简单休眠

    当进程休眠时,它将期待某个条件会在未来成为真。当一个休眠的进程被唤醒时,它必须再次检查它所等待的条件的确为真。Linux内核中最简单的休眠方式是称为wait_event的宏(以及它的几个变种);在实现休眠的同时,它也检查进程等待的条件。

    Wait_event(queue,condition);//非中断休眠,通常不用

    Wait_event_interruptible(queue,conditon);//常用,可被信号中断

    Wait_event_timeout(queue,condition,timeout);

    Wait_event_interruptible_timeout(queue,condition,timeout);

    其中,queue是等待队列头,condition是一个布尔表达式,上面的宏在休眠前后都要对该表达式求值;在条件为真之前,进程会休眠。

    唤醒进程

    Voidwake_up(wait_queue_head_t  *queue);//会唤醒等待在queue上的所有进程

    Voidwake_up_interruptible(wait_queue_head_t *queue);//只会唤醒那些执行可中断休眠的进程。

    在实践中,约定做法是在使用wait_event时使用wake_up,而在使用wait_event_interruptible时使用wake_up_interruptible。

    举例:任何试图从该设备上读取的进程均被置于休眠。只要某个进程向该设备写入,所有休眠的进程就会被唤醒。

    StaticDECLARE_WAIT_QUEUE_HEAN(wq);

    Static int flag=0;

    Ssize_sleep_read(structfile *filp, char __user *buf,size_t count, loff_t *pos)

    {

             printk(KERN_DEBUG “process %i  (%s) going to sleep ”,current->pid,current->com);

             wait_event_interruptible(wq,flag!=0);

             flag=0;

             printk(KERN_DEBUG “awaken %i  (%s) going to sleep ”,current->pid,current->com);

             return 0;

    }

    Ssize_sleep_write(structfile *filp, char __user *buf,size_t count, loff_t *pos)

    {

             printk(KERN_DEBUG “process %i  (%s) going to sleep ”,current->pid,current->com);

             flag=1;

             wake_up_interruptible(&wq);

             return count;

    }

    注意flag变量的使用,因为wait_event_interruptible要检查改变为真的条件,因此我们使用flag来构造这个条件。

    4.阻塞和非阻塞操作

    显式地非阻塞I/O由filp->f_flags中O_NONBLOCK标志决定。如果指定了O_NONBLOCK标志,read和write的行为就会有所不同。如果数据没有就绪时调用read或是在缓冲区没有空间时调用write,则该调用简单的返回-EAGAIN(try it again).例如,在磁带还没有插入时打开一个磁带设备通常会阻塞,如果磁带驱动程序是用O_NONBLOCK打开的,则不管磁带在不在,open都会立即成功返回。

     

  • 相关阅读:
    EF实现增删改查
    托管代码与非托管代码的区别
    堆、栈以及队列
    C#装箱和拆箱
    Leecode刷题之旅-C语言/python-349两个数组的交集
    Leecode刷题之旅-C语言/python-344反转字符串
    Leecode刷题之旅-C语言/python-217存在重复元素
    Leecode刷题之旅-C语言/python-206反转链表
    Leecode刷题之旅-C语言/python-204计数质数
    Leecode刷题之旅-C语言/python-203移除链表元素
  • 原文地址:https://www.cnblogs.com/riskyer/p/3266532.html
Copyright © 2020-2023  润新知