IPC note
IPC分类
通信、同步、信号
通信:
管道、FIFO、流socket
伪终端
消息队列、数据报socket
共享内存、内存映射(匿名映射、映射文件)
管道
写入数据量不超过PIPE_BUF字节的写入操作是原子的。
FIFO
FIFO在文件系统中拥有一个名称,并且其打开方式与打开一个普通文件相同。
#include <sys/stat.h>
int mkfifo(const char *pathname, mode_t mode);
默认情况下,打开一个FIFO以便读取数据(open() O_RDONLY标记)将会阻塞直到另一个进程打开FIFO以写入数据(open() O_WRONLY)为止。
相应地,打开一个FIFO以写入数据会阻塞直到另一个进程打开FIFO以读取数据。\
避免阻塞可以打开的时候添加O_NONBLOCK标记。
两个作用
- 允许单个进程先后打开同一个FIFO的两端。
- 防止两个FIFO的进程之间发生死锁。如X进程打开A再打开B,Y进程打开B再打开A。
select
http://janfan.cn/chinese/2015/01/05/select-poll-impl-inside-the-kernel.html
一遍轮询, 对每个文件描述符, 做一次struct file_operations结构体里的poll操作。
int do_select(int n, fd_set_bits *fds, struct timespec *end_time)
{
// …
for (;;) {
// …
for (i = 0; i < n; ++rinp, ++routp, ++rexp) {
// …
struct fd f;
f = fdget(i);
if (f.file) {
const struct file_operations *f_op;
f_op = f.file->f_op;
mask = DEFAULT_POLLMASK;
if (f_op->poll) {
wait_key_set(wait, in, out,
bit, busy_flag);
// 对每个fd进行I/O事件检测
mask = (*f_op->poll)(f.file, wait);
}
fdput(f);
// …
}
}
// 退出循环体
if (retval || timed_out || signal_pending(current))
break;
// 进入休眠
if (!poll_schedule_timeout(&table, TASK_INTERRUPTIBLE,
to, slack))
timed_out = 1;
}
}
(*f_op->poll)会返回当前设备fd的状态(比如是否可读可写),根据这个状态,do_select()接着做出不同的动作。
select()把全部fd检测一轮之后如果没有可用I/O事件,会让当前进程去休眠一段时间,等待fd设备或定时器来唤醒自己,然后再继续循环体看看哪些fd可用,以此提高效率。
缺点:
1.所能监视的文件描述符的数量有限制,sizeof(fd_set)=128,说明能监视的描述符的最大值为128*8=1024个
2.同时每次调用select都需要在内核遍历传递进来的所有fd,当fd很多时性能会下降
3.由于当有事件发生时,select返回后会修改三个事件集,所以,每次都需要把fd集合从用户区拷贝到内核区,当需要监视的fd数量增多时,性能会下降
poll
优点:
1.相比select来讲,它没有fd数量的限制,理论上打开fd的数目跟系统内在有关
2.也不用每次都把fd集合从用户区拷贝数据到内核,它使用一个 struct pollfd结构体来维护每个fd
缺点:
它本质上是和selece一样的,只是描述fd集合的方式不同,poll使用pollfd结构而不是select的fd_set结构,其他的都差不多
epoll
优点:
具备了select所不具备的所有优点
1.没有fd数量的限制,它所支持的fd上限是最大可以打开文件的数目,具体数目可cat/proc/sys/fs/file-max察看,一般来说这个数目和系统内存关系很大
2.epoll_ctl每次注册新的事件到epoll句柄中时(在epoll_ctl中指定EPOLL_CTL_ADD),会把所有的fd拷贝进内核,而不是在epoll_wait的时候重复拷贝,epoll保证了每个fd在整个过程中只会拷贝一次
3.epoll的解决方案不像select或poll一样每次都把current轮流加入fd对应的设备等待队列中,而只在epoll_ctl时把current挂一遍(这一遍必不可少)并为每个fd指定一个回调函数,当设备就绪,唤醒等待队列上的等待者时,就会调用这个回调函数,而这个回调函数会把就绪的fd加入一个就绪链表)。epoll_wait的工作实际上就是在这个就绪链表中查看有没有就绪的fd
适用场景:
当活动连接比较多的时候,epoll_wait的效率未必比select和poll高,因为此时回调函数被触发的过于频繁,因此epoll_wait适用于连接数量多,但活动连接较少的情况
epoll对文件描述符的操作有两种模式:LT和ET
LT(水平触发):这种模式是默认的工作模式,此时,epoll相当于效率较高的poll,当往epoll内核事件表中注册一个文件描述符上的EPOLLET事件时,epoll将以ET模式来操作该文件描述符
ET(边沿触发):ET模式是epoll的高效工作模式,它在很大程度上降低了同一个epoll事件被重复触发的次数