You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
内核模式:Unix/Linux 的内核实现了最核心的操作系统功能,包括进程管理、存储管理、文件管理等。而内核模式就是指直接运行在内核中的程序集合,在内核模式下,可执行代码有完全访问底层硬件的权限,可以执行任意的 CPU 指令,可以访问任意的内存地址。内核模式一般保留作为操作系统最受信任的访问者。内核的奔溃是最致命的,会导致整个计算机的停机。
目录
1 基本概念[Top]
在介绍IO多路复用之前,有一些 Unix/Linux 的基本概念需要理清楚:
内核模式:Unix/Linux 的内核实现了最核心的操作系统功能,包括进程管理、存储管理、文件管理等。而内核模式就是指直接运行在内核中的程序集合,在内核模式下,可执行代码有完全访问底层硬件的权限,可以执行任意的 CPU 指令,可以访问任意的内存地址。内核模式一般保留作为操作系统最受信任的访问者。内核的奔溃是最致命的,会导致整个计算机的停机。
用户模式:在内核以外的空间属于用户空间(或应用空间)。在用户空间内运行的程序集合就是用户模式。在用户模式下,可执行代码不能直接访问硬件和内存,而是必须通过一组原语来委托内核去访问底层硬件资源,这组原语叫做系统调用。用户模式的存在用于隔离操作系统的底层复杂性,同时保证系统的安全运行,因为用户空间的错误不会导致内核的奔溃,是可恢复的。
文件:在 Unix 中,文件是一个抽象概念,文件不同于具体的文档。在 Unix 中,一切外设和大部分进程间通信(IPC)都可以用文件来表示,所以业内有 “Unix 中一切皆文件” 的说法。可以这样说,在 Unix 中一切可以进行字节流读写的媒介都可以表示为文件,这包括普通的文档、磁盘、CD-ROM、键盘、鼠标、显示器、终端,以及用于进程间通信的管道(pipe)、socket 等等。这些文件还有一个共同点,那就是在 Unix 中都可以表示为一个全局的命名空间(文件路径),如:/usr/local 引用本地目录,使用 /home/joe/memo.pdf 引用一个文件,使用 /mnt/cdrom 引用 CD-ROM,使用 /tmp/mysql.sock 引用 UNIX 域套接字等。有了文件这一统一的抽象之后,可以大大简化操作系统的设计。
文件描述符:有了文件的概念,在实现中,Unix 需要一个能够表示文件的符号,即文件描述符,它是一个非负整数(通常是小整数),任何一个打开的文件都可以通过一个文件描述符来表示和访问。在逻辑层面,文件描述符类似于程序设计语言中的指针或者引用的概念。而在物理实现层面,同一个文件实际上可以由不同的文件描述符指代,既可以是不同进程中的文件描述符,也可以是相同进程中不同的文件描述符。具体而言,每个进程都会维护一张文件描述符表,表的每一行有两个属性:
[文件描述符, 文件指针]
,其中文件指针会指向系统全局的一个打开文件表,所有进程打开的文件的上下文信息都会在内核系统文件表中进行记录,每行表示一个文件上下文。进程级的文件指针会指向系统文件表中该文件对应的行。所以,给定一个文件描述符,内核就能找到该文件并对其进行读写。IO模型:进程之间的通信一般都要借助于文件。一般来说一个简单的 CS 通信模型的过程如下:先打开一个文件(或者多个,比如管道),进程 P1 往文件写数据;进程 P2 监控文件IO状态,一旦发现文件中有数据则将数据读出。这样就实现了简单的半双工通信功能——P2 向 P1 发送数据或通知。根据等待进程的行为以及通信的具体实现方式,可以分为以下几种IO模型:
2 IO多路复用技术[Top]
在 IO 多路复用技术中,通常有三种系统调用:select,poll,epoll。其中,select/poll 是传统 Unix 中的多路复用技术,其基本原理和性能都没有太大差别。而 epoll 是 Linux 中的专有 IO多路复用技术,相比 select/poll,在监控大量文件描述符的时候有显著的性能优势。
2.1 水平触发和边缘触发
当文件描述符准备就绪时,内核需要通知等待进程,通知的方式有两种:
所以,水平触发类似于发了工资后按需取用,并把剩下的钱作为存款,这样每个月都有钱花;而边缘触发类似于把每个月的工资基本花完,这样必须领到新的工资后才有钱花,所以也可以叫月光触发。
2.2 select
通常,调用 select 之后,等待进程会一直阻塞,直到一个或者多个文件描述符集合成为就绪态。select 系统调用的函数原型如下:
参数说明:
返回说明:
在每次调用 select 之前,都必须指定要监控的文件描述符,同时在调用完成后,指定的文件描述符集合都有可能被修改。所以每次调用前都需要重新初始化各个文件描述符集合。
2.3 poll
系统调用 poll 执行的任务同 select 很相似。两者间主要的区别在于我们要如何指定待检 查的文件描述符。在 select 中,我们提供三个集合,在每个集合中标明我们感兴趣的文件描述符。而在 poll 中我们提供一列文件描述符,并在每个文件描述符上标明我们感兴趣的事件。poll 的函数原型如下:
参数说明:
返回说明:
非零 revents 字段的 pollfd 结构体数量。
在每次调用完 poll 之后,通过检查就绪的文件描述符然后完成响应的 IO 操作后可以将该文件描述符对应的 revents 事件清零,这样在下一次调用 poll 的时候,无需再对文件描述符集合进行初始化。除此以外,select 和 poll 还有如下异同:
select/poll 存在的问题:
在检查大量文件描述符时,两个系统调用有如下的一些性能问题:
产生以上问题的主要原因是,从内核的角度来看,每次从用户空间调用 select/poll 传递给内核的都是一系列全新的文件描述符,于是内核要去一一检查一遍,在超时范围内检查结束之后便放弃对这些文件描述符的监控,这样等到下次再调用 select/poll 时,内核又当做是一些新的文件描述符进行重新检查。而实际情况往往是程序重复调用这些系统调用所检查的文件描述符集合都是相同的。所以如果提前指示内核把这些文件描述符记录下来,告诉内核默认情况下下次到来还是检查这些文件描述符,那么内核只需要持续监控这些文件描述符的状态即可,只要有一个文件描述符的IO发生变化便向等待进程发送就绪通知。这样内核检查文件描述符的时间复杂度从 O(n) 降到了 O(1)。Linux 的 epoll 系统调用便是基于此思想实现的。
2.4 epoll
epoll 相对 select/poll 来说,当检查大量的文件描述符时,epoll 的性能比 select/poll 高很多;epoll 既支持水平触发也支持边缘触发,而 select/poll 只支持水平触发。
epoll 基本流程如下:
参数 size 指定了我们想要通过 epoll 实例来检查的文件描述符个数。该参数只是一个估计值,无需精确指定。
该函数返回一个代表 epoll 实例的文件描述符 epfd,epfd 在 epoll_ctl 和 epoll_wait 中都需要用到,整个 epoll 机制都需要在 epoll 实例上进行操作。
参数说明:
epfd:表示 epoll 实例的文件描述符。
fd:指定感兴趣的文件描述符。
op:指定需要执行的操作,取值如下:
ev:指向结构体 epoll_event 的指针。
epoll_wait
参数说明:
调用成功后,epoll_wait()返回数组 evlist 中的元素个数。如果在 timeout 超时间隔内没有任何文件描述符处于就绪态的话,返回 0。出错时返回−1。
2.4 epoll 与 select/poll 性能对比
epoll 比 select/poll 在监控大量文件描述符时性能表现优越的原因如下:
参考[Top]
Linux-Unix系统编程手册(第4、5、63章)
The text was updated successfully, but these errors were encountered: