linux中的阻塞机制及等待队列

2023-10-27

阻塞与非阻塞是设备访问的两种方式。驱动程序需要提供阻塞(等待队列,中断)和非阻塞方式(轮询,异步通知)访问设备。在写阻塞与非阻塞的驱动程序时,经常用到等待队列。


 

一、阻塞与非阻塞

阻塞调用是没有获得资源则挂起进程,被挂起的进程进入休眠状态,调用的函数只有在得到结果之后才返回,进程继续。

非阻塞是不能进行设备操作时不挂起,或返回,或反复查询,直到可以进行操作为止,被调用的函数不会阻塞当前进程,而会立刻返回。

对象是否处于阻塞模式和函数是不是阻塞调用有很强的相关性,但并不是一一对应的。阻塞对象上可以有非阻塞的调用方式,我们可以通过一定的API去轮询 状态,在适当的时候调用阻塞函数,就可以避免阻塞。而对于非阻塞对象,调用的函数也可以进入阻塞调用。函数select()就是这样一个例子。

驱动程序常需要这种能力:当应用程序进程read(),write()等系统调用时,若设备的资源不能获取,而用户又希望以阻塞的方式访问设备,驱动程序应该在设备驱动程序的xxx_read(),xxx_write()等操作中将进程阻止到资源可以获取,以后,应用程序的read(),write()等调用返回,整个过程仍然进行了正确的设备访问,用户并没有感知到。若用户以非阻塞的方式访问设备文件,则当设备资源不可获取时,设备驱动的xxx_read(),xxx_write()等操作应立即返回,read(),write()等喜用调用也随即被访问。

阻塞不是低效率,如果设备驱动不阻塞,用户想获取设备资源只能不断查询,小号CPU资源,阻塞访问时,不能获取资源的进程将进入休眠,将CPU资源让给其他资源。

阻塞的进程会进入休眠状态,因此,必须确保有一个地方能唤醒休眠的进程。唤醒进程的地方最大可能发生在终端里面,因为硬件资源的获得往往伴随着一个终端。

对象是否处于阻塞模式和函数是不是阻塞调用有很强的相关性,但并不是一一对应的。阻塞对象上可以有非阻塞的调用方式,我们可以通过一定的API去轮询状 态,在适当的时候调用阻塞函数,就可以避免阻塞。而对于非阻塞对象,调用的函数也可以进入阻塞调用。函数select()就是这样一个例子。

二、等待队列

在linux驱动程序中,可使用等待队列(wait queue)来实现阻塞进程的唤醒,以队列为基础数据结构,与进程调度机制紧密结合,用于视线内核的异步事件通知机制,也可用于同步对系统资源的访问。(信号量在内核中也依赖等待队列来实现)

在软件开发中任务经常由于某种条件没有得到满足而不得不进入睡眠状态,然后等待条件得到满足的时候再继续运行,进入运行状态。这种需求需要等待队列机制的支持。Linux中提供了等待队列的机制,该机制在内核中应用很广泛。

等待队列在linux内核中有着举足轻重的作用,很多linux驱动都或多或少涉及到了等待队列。因此,对于linux内核及驱动开发者来说,掌握等待队 列是必须课之

Linux内核的等待队列是以双循环链表为基础数据结构,与进程调度机制紧密结合,能够用于实现核心的异步事件通知机制。它有两种数据结构:等待队列头 (wait_queue_head_t)和等待队列项(wait_queue_t)。等待队列头和等待队列项中都包含一个list_head类型的域作为”连接件”。它通过一个双链表和把等待task的头,和等待的进程列表链接起来。下面具体介绍。

1.定义

头文件:/include/linux/wait.h

 

struct __wait_queue_head {
      spinlock_t lock;                    /* 保护等待队列的原子锁 (自旋锁),在对task_list与操作的过程中,使用该锁实现对等待队列的互斥访问*/
      struct list_head task_list;          /* 等待队列,双向循环链表,存放等待的进程 */
};

/*__wait_queue,该结构是对一个等待任务的抽象。每个等待任务都会抽象成一个wait_queue,并且挂载到wait_queue_head上。该结构定义如下:*/

struct __wait_queue {
 unsigned int flags;
#define WQ_FLAG_EXCLUSIVE   0x01
void *private; /* 通常指向当前任务控制块 */ wait_queue_func_t func; struct list_head task_list; /* 挂入wait_queue_head的挂载点 */ };
typedef struct __wait_queue wait_queue_t;
/* 任务唤醒操作方法,该方法在内核中提供,通常为auto remove_wake_function */

 

2.操作:

(1) 定义并初始化"等待队列头"

wait_queue_head_t my_queue;  
init_waitqueue_head(&my_queue);  //会将自旋锁初始化为未锁,等待队列初始化为空的双向循环链表。
//宏名用于定义并初始化,相当于"快捷方式"
DECLARE_WAIT_QUEUE_HEAD (my_queue);  

(2) 定义"等待队列"

/*定义并初始化一个名为name的等待队列 ,注意此处是定义一个wait_queue_t类型的变量name,并将其private与设置为tsk*/
DECLARE_WAITQUEUE(name,tsk);
struct __wait_queue {
 unsigned int flags;
#define WQ_FLAG_EXCLUSIVE   0x01
 void *private;                       /* 通常指向当前任务控制块 */
 wait_queue_func_t func;             
 struct list_head task_list;              /* 挂入wait_queue_head的挂载点 */
};
typedef struct __wait_queue wait_queue_t;

其中flags域指明该等待的进程是互斥进程还是非互斥进程。其中0是非互斥进程,WQ_FLAG_EXCLUSIVE(0×01)是互斥进程。等待队列 (wait_queue_t)和等待对列头(wait_queue_head_t)的区别是等待队列是等待队列头的成员。也就是说等待队列头的task_list域链接的成员就是等待队列类型的(wait_queue_t)。

 

(3) (从等待队列头中)添加/删除等待队列

//设置等待的进程为非互斥进程,并将其添加进等待队列头(q)的队头中。
void add_wait_queue(wait_queue_head_t *q, wait_queue_t *wait)
{
    unsigned long flags;
    wait->flags &= ~WQ_FLAG_EXCLUSIVE;
    spin_lock_irqsave(&q->lock, flags);
    __add_wait_queue(q, wait);
    spin_unlock_irqrestore(&q->lock, flags);
}
EXPORT_SYMBOL(add_wait_queue);

//下面函数也和add_wait_queue()函数功能基本一样,只不过它是将等待的进程(wait)设置为互斥进程。
void add_wait_queue_exclusive(wait_queue_head_t *q, wait_queue_t *wait)
{
    unsigned long flags;
    wait->flags |= WQ_FLAG_EXCLUSIVE;
    spin_lock_irqsave(&q->lock, flags);
    __add_wait_queue_tail(q, wait);
    spin_unlock_irqrestore(&q->lock, flags);
    }
    EXPORT_SYMBOL(add_wait_queue_exclusive);
//在等待的资源或事件满足时,进程被唤醒,使用该函数被从等待头中删除。
void
remove_wait_queue(wait_queue_head_t *q, wait_queue_t *wait) { unsigned long flags; spin_lock_irqsave(&q->lock, flags); __remove_wait_queue(q, wait); spin_unlock_irqrestore(&q->lock, flags); } EXPORT_SYMBOL(remove_wait_queue);

(4) 等待事件

#define wait_event(wq, condition)                  
do {                                   
        if (condition)                         
         break;                         
        __wait_event(wq, condition);                   
} while (0)
wait_event(queue,condition);等待以queue为等待队列头等待队列被唤醒,condition必须满足,否则阻塞  
wait_event_interruptible(queue,condition);可被信号打断  
wait_event_timeout(queue,condition,timeout);阻塞等待的超时时间,时间到了,不论condition是否满足,都要返回  
wait_event_interruptible_timeout(queue,condition,timeout) 

wait_event()宏:

在等待会列中睡眠直到condition为真。在等待的期间,进程会被置为TASK_UNINTERRUPTIBLE进入睡眠,直到condition变量变为真。每次进程被唤醒的时候都会检查condition的值.

wait_event_interruptible()函数:

和wait_event()的区别是调用该宏在等待的过程中当前进程会被设置为TASK_INTERRUPTIBLE状态.在每次被唤醒的时候,首先检查 condition是否为真,如果为真则返回,否则检查如果进程是被信号唤醒,会返回-ERESTARTSYS错误码.如果是condition为真,则 返回0.

wait_event_timeout()宏:

也与wait_event()类似.不过如果所给的睡眠时间为负数则立即返回.如果在睡眠期间被唤醒,且condition为真则返回剩余的睡眠时间,否则继续睡眠直到到达或超过给定的睡眠时间,然后返回0.

wait_event_interruptible_timeout()宏

与wait_event_timeout()类似,不过如果在睡眠期间被信号打断则返回ERESTARTSYS错误码.

wait_event_interruptible_exclusive()宏

同样和wait_event_interruptible()一样,不过该睡眠的进程是一个互斥进程.

(5)唤醒队列

/*
 __wake_up - wake up threads blocked on a waitqueue.
 @q: the waitqueue
 @mode: which threads
 @nr_exclusive: how many wake-one or wake-many threads to wake up
 @key: is directly passed to the wakeup function
*/
void __wake_up(wait_queue_head_t *q, unsigned int mode,
                int nr_exclusive, void *key)
{
    unsigned long flags;
    spin_lock_irqsave(&q->lock, flags);
    __wake_up_common(q, mode, nr_exclusive, 0, key);
    spin_unlock_irqrestore(&q->lock, flags);
}
    EXPORT_SYMBOL(__wake_up);
//唤醒等待队列.可唤醒处于TASK_INTERRUPTIBLE和TASK_UNINTERUPTIBLE状态的进程,和wait_event/wait_event_timeout成对使用

#define wake_up_interruptible(x)    __wake_up(x, TASK_INTERRUPTIBLE, 1, NULL)
//和wake_up()唯一的区别是它只能唤醒TASK_INTERRUPTIBLE状态的进程.,与wait_event_interruptible
wait_event_interruptible_timeout
wait_event_interruptible_exclusive成对使用

#define wake_up_all(x)          __wake_up(x, TASK_NORMAL, 0, NULL) 
#define wake_up_interruptible_nr(x, nr) __wake_up(x, TASK_INTERRUPTIBLE, nr, NULL)
#define wake_up_interruptible_all(x)    __wake_up(x, TASK_INTERRUPTIBLE, 0, NULL)
//这些也基本都和wake_up/wake_up_interruptible一样

wake_up()与wake_event()或者wait_event_timeout成对使用,
wake_up_intteruptible()与wait_event_intteruptible()或者wait_event_intteruptible_timeout()成对使用。

(6) 在等待队列上睡眠:

sleep_on()函数

void __sched sleep_on(wait_queue_head_t *q)
{  
  sleep_on_common(q, TASK_UNINTERRUPTIBLE, MAX_SCHEDULE_TIMEOUT);
}

static long __sched
sleep_on_common(wait_queue_head_t *q, int state, long timeout)
{
        unsigned long flags;
        wait_queue_t wait;
        init_waitqueue_entry(&wait, current);
        __set_current_state(state);
        spin_lock_irqsave(&q->lock, flags);
        __add_wait_queue(q, &wait);
        spin_unlock(&q->lock);
        timeout = schedule_timeout(timeout);
        spin_lock_irq(&q->lock);
        __remove_wait_queue(q, &wait);
        spin_unlock_irqrestore(&q->lock, flags); 
        return timeout;
}

该函数的作用是定义一个等待队列(wait),并将当前进程添加到等待队列中(wait),然后将当前进程的状态置为 TASK_UNINTERRUPTIBLE,并将等待队列(wait)添加到等待队列头(q)中。之后就被挂起直到资源可以获取,才被从等待队列头(q) 中唤醒,从等待队列头中移出。在被挂起等待资源期间,该进程不能被信号唤醒。

sleep_on_timeout()函数

long __sched sleep_on_timeout(wait_queue_head_t *q, long timeout)
{
       return sleep_on_common(q, TASK_UNINTERRUPTIBLE, timeout);
}
EXPORT_SYMBOL(sleep_on_timeout);

与sleep_on()函数的区别在于调用该函数时,如果在指定的时间内(timeout)没有获得等待的资源就会返回。实际上是调用 schedule_timeout()函数实现的。值得注意的是如果所给的睡眠时间(timeout)小于0,则不会睡眠。该函数返回的是真正的睡眠时间。

interruptible_sleep_on()函数

 void __sched interruptible_sleep_on(wait_queue_head_t *q)
{
        sleep_on_common(q, TASK_INTERRUPTIBLE, MAX_SCHEDULE_TIMEOUT);
}
EXPORT_SYMBOL(interruptible_sleep_on);

该函数和sleep_on()函数唯一的区别是将当前进程的状态置为TASK_INTERRUPTINLE,这意味在睡眠如果该进程收到信号则会被唤醒。

interruptible_sleep_on_timeout()函数:

long __sched
interruptible_sleep_on_timeout(wait_queue_head_t *q, long timeout)
{
    return sleep_on_common(q, TASK_INTERRUPTIBLE, timeout);
}
EXPORT_SYMBOL(interruptible_sleep_on_timeout);

类似于sleep_on_timeout()函数。进程在睡眠中可能在等待的时间没有到达就被信号打断而被唤醒,也可能是等待的时间到达而被唤醒。

以上四个函数都是让进程在等待队列上睡眠,不过是小有诧异而已。在实际用的过程中,根据需要选择合适的函数使用就是了。例如在对软驱数据的读写中,如果设 备没有就绪则调用sleep_on()函数睡眠直到数据可读(可写),在打开串口的时候,如果串口端口处于关闭状态则调用 interruptible_sleep_on()函数尝试等待其打开。在声卡驱动中,读取声音数据时,如果没有数据可读,就会等待足够常的时间直到可读取。

 

转载于:https://www.cnblogs.com/gdk-0078/p/5172941.html

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

linux中的阻塞机制及等待队列 的相关文章

  • xss-level1

    首先搭建xss靶场 打开浏览器输入地址来到第一关 这里我先查看了一下源代码 先试一下弹出会话框 name lt
  • 程序跑飞的如何查问题

    在下这厢有礼了 最近一直在调试公司的代码 调的我有点慢 给自己总结一下 我是在FPGA上调试 一个通信交互的工程 我遇到程序跑飞的无非是三种情况 1 数组越界 就是数组的大小只有array 100 但是那你用了array 500 产生越界
  • rk3368 开机内核启动不了

    Platform RK3368 OS Android 6 0 Kernel 3 10 0 电源管理芯片用的是配套的rk818 经测量发现板子在上电启动时 u boot阶段与kernel阶段dcdc电压不一样 从uboot切换到kernel时
  • 矩阵特征值与行列式、迹的关系

    矩阵的特征值之和等于矩阵的行列式 矩阵的特征值之积等于矩阵的迹 简单的理解证明如下 1 二次方程的韦达定理 请思考 x 2 bx c 0 这个方程的所有根的和等于多少 所有根的积等于多少 2 把二次方程推广到 N 次
  • Linux下安装opencv with-ffmpeg解决无法读取视频的问题

    Linux下安装opencv with ffmpeg解决无法读取视频的问题 参考文章 1 Linux下安装opencv with ffmpeg解决无法读取视频的问题 2 https www cnblogs com haiyang21 p 1
  • 求最大公约数,最小公倍数(c++)

    文章目录 最大公约数 质数和合数 公约数 计算最大公约数 辗转相除法 最小公倍数 最大公约数 质数和合数 质数也称素数 指大于1 并且除了1和它自己 不能被任何其他自然数整除的数 除了1和质数的其他自然数称为合数 合数必定可以分解成2个或以
  • Spring Cloud中的服务注册和发现是怎样实现的?Spring Boot和Spring Cloud的关系是怎样的?Spring的核心容器包括哪些模块?Spring的Bean作用域有哪些?它们的区

    1 Spring Cloud中的服务注册和发现是怎样实现的 在Spring Cloud中 服务注册和发现是通过Eureka来实现的 Eureka是Netflix开源的一个服务治理组件 用于实现服务注册和发现的功能 具体来说 服务的提供方会在
  • vue3使用jodit富文本编辑器,自定义各项配置及组件封装

    目录 常用配置 设置中文 字体设置 CDN的引用 图片上传 对编辑器中生成的元素添加默认属性 组件封装 本文使用时的版本 vue 3 2 36 jodit 3 24 7 Jodit 是国外编写的一个功能强大的富文本编辑器 有常规版本和PRO
  • “数据库事务(Database Transaction)

    事务的使用 关于事务 我今天要把自己放在一个初学者的心态来写这篇文章 之前几篇文章大多讲的是对于Winner的应用 今天要从根本上来讲 一下 事务 以及事务在Winner中的应用 首先从基础讲起 什么是 事务 事务能帮我们解决哪些问题 摘录
  • 每日一解 戳气球(困难的动归)

    题目 戳气球 有 n 个气球 编号为0 到 n 1 每个气球上都标有一个数字 这些数字存在数组 nums 中 现在要求你戳破所有的气球 如果你戳破气球 i 就可以获得 nums left nums i nums right 个硬币 这里的
  • Unit Test 5--编写第一个单元测试

    大家好 我是神韵 是一个技术 生活博主 出文章目的主要是两个 一是好记忆不如烂笔头 记录总结中提高自己 二是希望我的文章可以帮到大家 欢迎来点赞打卡 你们的行动将是我无限的动力 本篇主题是 编写第一个单元测试 其它文章链接 Unit Tes
  • 几个小程序

    1 求变量val中1的个数 方法1 逐个去比较 int getNum1 int val int num 0 while val num val 0x01 val val gt gt 1 return num 方法2 使用x x 1 int
  • pycharm中通过命令行运行程序,进行程序调试

    1 点击菜单栏run gt edit configurations 2 如果命令行输入python ecode py users items 则在parameters框中输入 users items
  • Ubuntu下启动idea的方法

    最近在ubuntu下部署网站 使用IDEA进行开发 但是平时使用linux比较少 所以一路上踩到了很多坑 光是idea的启动就折腾了半天 后来发现是输入的命令有问题 安装好IDEA以后 应该进入安装目录的bin文件夹下 关键点来了 linu
  • K8S 网络问题导致 ns 相关的服务不能互相访问

    背景 近期重新部署了一套K8S环境 是基于本机虚拟机 采用 Kuboard Spray 方式 使用 KuboardSpray 安装kubernetes v1 23 1 Kuboard 安装成功后 并无感觉不妥 看到 pod 状态都是 run
  • 初探强化学习

    1 引言 人生中充满选择 每次选择就是一次决策 我们正是从一次次决策中 把自己带领到人生的下一段旅程中 在回忆往事的时候 我们会对生命中某些时刻的决策印象深刻 还好当时选择了读研 毕业后找到了一份自己喜欢的工作 如果当初接受那家公司的off
  • Docker : Docker 查看容器 IP 地址以及相关信息

    1 美图 2 查看Docker的底层信息 docker inspect 会返回一个 JSON 文件记录着 Docker 容器的配置和状态信息 base lcc lcc docker inspect es4 Id 4b1215a95bf712
  • js深拷贝与浅拷贝

    区别 浅拷贝只是增加了一个指针指向已存在的内存地址 仅仅是指向被复制的内存地址 如果原地址发生改变 那么浅拷贝复制出来的对象也会相应的改变 深拷贝是增加了一个指针并且申请了一个新的内存 使这个增加的指针指向这个新的内存 浅拷贝 一 Java
  • C++ 如何从string中删除一个字符

    使用编程软件 dev5 4 0 方法一 使用iterator和erase 删除与 it 所指字符第一个相同的 注 经反应将第一个代码改了下并在VS2017里运行通过了 代码如下 include
  • Jenkins 从小白入门到企业实践打怪放弃之路系列笔记 【持续集成与交付快速入门必备】

    我在B站学运维之Jenkins持续集成和交付快速入门介绍与安装 1 https www bilibili com read cv13512558 我在B站学运维之Jenkins持续集成和交付入门基础使用与集成部署实践 2 https www

随机推荐

  • Reason: No converter found capable of converting from type [java.lang.String] to type [java.util.Map

    Reason No converter found capable of converting from type java lang String to type java util Map
  • mmdetection源码笔记(五):测试之test.py的解读

    引言 在test阶段有以下几个方法 single gpu test 顾名思义 就是单GPU测试 该方法在main 中调用 当不分布式测试的时候 则运行次测试方法 该方法的实现中 其实是调用了检测器测试过程的forward 前向计算过程 以c
  • redis如何配置哨兵集群?

    前言 前面我们介绍了主从复制集群模式 但是存在一个问题 redis在主从集群中好像没有共识算法 比如raft 那么在master挂掉的情况下 就无法从几个slave节点中自主选举出一个新的 master 节点 redis提供了新的方案 哨兵
  • 外部中断和内部中断 硬中断 软中断 中断下半部

    重要声明 以下代码有粘贴 截取他人劳动成果的成分 如有雷同 不胜荣幸 如您不能容忍 请不要独自忍受 weChat iisssssssssii 联系小民 主动删除 中断含义 中断是CPU处理外部突发事件的一个重要技术 它能使CPU在运行过程中
  • QT不允许使用不完整的类型出现原因

    目前遇到的情况如下 1 没有引用头文件 2 没有设置头文件所在路径 或设置路径错误 3 头文件定义出错 此种情况比较常见于新建一个头文件时为了方便直接拷贝已有的头文件来修改 但是图中绿色框框部分没有修改 如下两张图所示 b h由a h复制而
  • 下载pip、安装pip

    下载pip 安装pip 码农家园 Installation pip documentation v23 2 1 如何在 Mac 上安装 PIP https m php cn faq 517564 html pip3 install pyot
  • 一.LLVM基础库初探

    LLVM和Clang被精心组织成如下库 1 libLLVMCore 它包含了与LLVM IR相关的逻辑 IR构造 数据布局 指令 基础块和函数 和IR验证器 它还提供了pass管理器 2 libLLVMAnalysis 将多个IR分析步骤进
  • Maven安装教程详解

    一 准备工作 1 确定电脑上已经成功安装jdk7 0以上版本 2 win10操作系统 3 maven安装包 下载地址 http maven apache org download cgi 二 解压Maven安装包 在上述地址中下载最新的Ma
  • DNS与CDN——前端重点

    DNS的基础知识 1 统一资源定位符 URL 也被称作 网址 用于定位互联网上的资源 2 DNS Domain Name System DNS具体Query过程 3 DNS记录 资源记录 Resource Record DNS的数据库条目
  • 2018-9-11-vue的日期输入 from 表单类型

    注意这个格式
  • mapper时间条件查询

    select
  • Logback configuration error detected: 怎么解决?(亲测有效)

    发生这个错误时首先确认你的xml文件内容是没有问题的 然后检查一下你的properties文件的classpath的配置有没有错 可能的错误如下 错误报没有找到那么就是classpath写错了 或者找到了报IlllegalException
  • C++ 内置类型和类类型的默认初始化和值初始化

    如果定义变量时候没有指定初值 则变量被默认初始化 但是默认初始化还有另外一个含义 在定义内置类型时由于定义变量的位置不同 有可能会不发生初始化操作 这种情况下称为默认初始化 也有可能会给定一个初值 称为值初始化 由上 个人琢磨其实默认初始化
  • 下载文件获取的contentLength 的值是-1的问题

    项目中加了个下载方法 获取的响应中的contentlength值总是 1 查看源码可以看到 用这个int类型的获取的当length大于Int的最大值 2147483647 就返回 1 我需要下载的文件确实大于这个值了 查看源码还有别的获取l
  • 数据库系统-第二章关系数据库 复习

    关系操作 关系数据库基础 关系代数 这个 的写法是错的 因为不存在一个元组的cno既等于c2又等于c4
  • UE4 性能优化方法(工具篇)

    官方文档链接 https docs unrealengine com latest CHN Engine Performance index html 本文依据UE4官方文档以及官方博客等总结而来 可能不全面 后面会陆续添加 内置工具的详细
  • 将Servlet中的ResultSet显示到Jsp页面

    Servlet从数据库中拿到结果以后 对用户来说 他们并不关心你怎么拿的 恰恰最重要的是 他们要看到信息展示在网页上 那这个该怎么实现 言归正传 拿到数据以后 想要把数据展示到Jsp页面上 这样办 跳转 转发和重定看大佬文章 清晰又明白 转
  • vscode连接服务器不用每次都输入密码

    1 首先在自己的本地生成公钥和密钥 git bash 输入以下命令 ssh keygen 然后一路回车 就会在自己用户 ssh文件夹下生成一对密钥 生成的公钥和密钥默认放在 ssh文件夹 我的是 2 修改本地的配置文件 添加下面这行属性到配
  • python学习笔记---IO编程【廖雪峰】

    IO编程 IO在计算机中指Input Output 也就是输入和输出 由于程序和运行时数据是在内存中驻留 由CPU这个超快的计算核心来执行 涉及到数据交换的地方 通常是磁盘 网络等 就需要IO接口 IO编程中 Stream 流 是一个很重要
  • linux中的阻塞机制及等待队列

    阻塞与非阻塞是设备访问的两种方式 驱动程序需要提供阻塞 等待队列 中断 和非阻塞方式 轮询 异步通知 访问设备 在写阻塞与非阻塞的驱动程序时 经常用到等待队列 一 阻塞与非阻塞 阻塞调用是没有获得资源则挂起进程 被挂起的进程进入休眠状态 调