Chinaunix首页 | 论坛 | 博客
  • 博客访问: 26110
  • 博文数量: 11
  • 博客积分: 25
  • 博客等级: 民兵
  • 技术积分: 70
  • 用 户 组: 普通用户
  • 注册时间: 2012-09-24 21:30
个人简介

有点儿。。。。。

文章分类

全部博文(11)

文章存档

2017年(1)

2016年(10)

我的朋友
最近访客

分类: LINUX

2016-07-21 11:07:28

阻塞与非阻塞

    阻塞调用是指在调用结果返回前,挂起当前进程;知道结果返回后才返回。

    同步和阻塞的差异:同步仅仅强调逻辑上先后,及逻辑上没有返回,但不一定挂起当前线程。

    非阻塞调用是指在不能立刻得到结果时,立刻返回,不会挂起当前线程。

对象阻塞状态与阻塞调用的关系

    对象处于阻塞状态与阻塞调用有一定的相关性,但不是一一对应关系。阻塞对象可以使用非阻塞调用,可以
    通过轮询方式查看,然后调用阻塞函数,这样避免阻塞;非阻塞对象也可以使用阻塞的调用方式,例如
    
select系统调用。


挂起(休眠)

    休眠意味着线程被标志为特殊状态并从运行队列移除。直到某些情况修改了这一状况,才重新调度执行。休
    眠的线程被搁置一边,等待某个事件发生。

    安全进入休眠要记住个2规则:

    第一不要在原子上下文进入休眠。因此驱动中此时不能拥有自旋锁,seqlock, RCU锁。禁止中断的情况下也
    不能休眠。拥有信号量进入休眠的代码必须很短,并且保证信号量不能阻塞唤醒我们自己就的线程。


    第二唤醒是
必须检查等待的条件必须为真。

    另外必须有地方唤醒,否则不能睡眠;唤醒源必须能找到睡眠线程。因此引入等待队列。

等待队列

    等待队列用处很多,尤其是中断处理、进程同步、定时等场合。可以使用等待队列唤醒进程的唤醒.

  • 定义等待队列头

    wait_queue_head_t my_queue;

  • 初始化等待队列头


     init_waitqueue_head( my_queue)

    或静态定义并初始化:DECLARE_WAIT_QUEUE_HEAD(name)

  • 定义等待队列    
    DECLARE_WAITQUEUE(name, tsk)
  • 添加/删除等待队列
点击(此处)折叠或打开
  1.     void add_wait_queue(wait_queue_head_t *q, wait_queue_t *wait);
  2.     void add_wait_queue_exclusive(wait_queue_head_t *q, wait_queue_t *wait);
  3.     void remove_wait_queue(wait_queue_head_t *q, wait_queue_t *wait)
  • 等待事件

点击(此处)折叠或打开

  1. wait_event(queue, condition)
  2. wait_event_interruptible(queue, condition)
  3. wait_event_timeout(queue, condition, timeout)
  4. wait_event_interruptible_timeout(queue, condition, timeout)


  • 唤醒

点击(此处)折叠或打开

  1.     void wake_up(wait_queue_head_t *queue)
  2.     void wake_up_interruptible(wait_queue_head_t *queue)
  • 在等待队列上睡眠  
  sleep_on函数将当前进程状态设置为TASK_UNINTERRUPTIBLE,并定义一个等待队列将其链入等待队列头q,等待唤
  醒。
  
点击(此处)折叠或打开
  1.     void sleep_on(wait_queue_head_t *q);
  2.     long sleep_on_timeout(wait_queue_head_t *qsigned long timeout);
  3.     void interruptible_sleep_on(wait_queue_head_t *q);
  4.     long interruptible_sleep_on_timeout(wait_queue_head_t *q, signed long timeout);
轮询操作
    轮询操作也是和阻塞非阻塞I/O相关的一个议题。轮询在支持非阻塞访问的应用程序中用来监测访问资源是否能进行非阻塞访问,可以监测多个访问资源。一般使用select/poll系统调用来实现,驱动层也要实现对应poll接口

  • select系统调用相关介绍

    系统提供select函数来实现多路复用输入/输出模型。原型:


    #include <linux/poll.h>
    int select(int maxfd,fd_set *rdset,fd_set *wrset,fd_set *exset,struct timeval *timeout); 

    参数maxfd是需要监视的最大的文件描述符值+1;

     rdset,wrset,exset分别对应于需要检测的可读文件描述符的集合,可写文件描述符的集合及异常文件描述符
    
struct timeval结构用于描述一段时间长度,如果在这个时间内,需要监视的描述符没有事件发生则函数返
    
回,返回值为0。 

     返回值正整数,表示满足的文件描述符的个数.

    返回0,表示超时.

    返回-1,且errno为 -EENTIR,表示select在阻塞时被信号中断.

    返回-1,其他情况下的错误返回,会设置相应errno.



  • 文件描述符集合说明
    FD_ZERO(fd_set *fdset):将指定的文件描述符集清空,在对文件描述符集合进行设置前,必须对其进行初始化,如果不清
    空,由于在系统分配内存空间后,通常并不作清空处理,所以结果是不可知的。


    FD_SET(int fd,fd_set *fdset);用于在文件描述符集合中增加一个新的文件描述符。
    FD_CLR(fd_set *fdset);用于在文件描述符集合中删除一个文件描述符。

    FD_ISSET(int fd,fd_set *fdset);用于测试指定的文件描述符是否在该集合中。 


  • 驱动层poll的实现

    unsigned int poll(struct file *filp,poi_table *wait);

    poll设备方法负责完成:

    • 使用poll_wait将等待队列添加到poll_table中.

    void poll_wait(struct file *,wait_queue_head_t *,poll_table *);
     该函数不会挂起当前进程

    • 返回描述设备是否可读或可写的掩码. 

    POLLIN 设备可读

    POLLRDNORM 数据可读

    POLLOUT 设备可写

    POLLWRNORM 数据可写

    设备可读一般返回(POLLIN|POLLRDNORM),设备可写一般返回(POLLOUT|POLLWRNORM).

  • POLL工作原理解析
    在设备的poll方法的实现过程中,会发现并没有将进程阻塞的语句,这是因为Poll方法只是做一个登记,真正的阻塞在
    fs/select.c中的do_select函数.do_select是select系统调用对应的内核函数.do_select根据poll方法返回值来进行判断,
    如果条件不满足,那么do_select中会调用poll_schedule_timeout将进程设置为可中断睡眠.


















阅读(1346) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~