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标记。

两个作用

  1. 允许单个进程先后打开同一个FIFO的两端。
  2. 防止两个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事件被重复触发的次数

https://zhuanlan.zhihu.com/p/22834126

posted @ 2020-04-27 11:32  我在地狱  阅读(176)  评论(0编辑  收藏  举报