管道
命名管道(named pipe)
Linux中的命名管道 (named pipe) 是一种特殊类型的文件,又称为FIFO,它外的外在表现好像是一个磁盘文件,实际上本质只是一个数据流或缓冲区。管道允许一个进程的输出直接作为另一个进程的输入,在Linux系统中是一种重要的实现进程间通信(IPC)的机制。
命名管道创建的管道文件不是普通的磁盘文件。这个文件本身不存储数据(它不占用磁盘空间来存储长期数据),而是作为一个通信接口存在 (它是一个文件系统标识, 方便我们以操作文件系统的方式读写管道的缓冲区)。
命名管道, 从一端读取数据, 从另一端写入数据。使用命名管道, 必须读端写端同时打开, 无法单独去打开读端或者写端。
半双工是可以从管道来或回(同一时刻),但一般拿单个named pipe作单工,即只来或只回。
管道缓冲区无数据,读管道阻塞;写满则写管道阻塞。
管道缓冲区还有数据,写端关闭,读端也可以读完剩余的数据。无剩余则read返回0。
读端关闭时,写端还写会导致write操作触发SIGPIPE信号,导致进程异常终止。
mkfifo name.pipe//创建named pipe
构建IO多路复用的全双工(select)
//俩程序,以下为其中一个。
int write_fd = open("1.pipe",O_WRONLY);//先打开管道,注意顺序避免死锁。
int read_fd = open("2.pipe",O_RDONLY);
fd_set set;//select部分的套路
while(1){
FD_ZERO(&set);//1
FD_SET(STDIN_FILENO, &set);//2
FD_SET(read_fd, &set);//2
select(10,NULL,NULL,NULL,NULL);//3
//文件描述符个数+1,读fd们,写fd们,异常fd们,监听阻塞的时间(NULL为等指定fd就绪,0为不等待检查fd直接返回);
if(FD_ISSET(fd_read, &set)){.....}//有东西过来可以读了(执行某些操作)
if(FD_ISSET(STDIN_FILENO,&SET){......}//写了东西(执行某些操作)
int read_stdin = read(STDIN_FILENO, buf, sizeof(buf));//读标准输入的,传到buff
write(fd_write, buf, sizeof(buf));//将buff的写进管道
}
select底层逻辑:
创建监听集合fd_set, 并初始化(FD_ZERO)
- 将要监听的fd放入fd_set集合中
- 调用select开始监听:
- 把处于用户态的监听集合拷贝到内核态空间
- 内核进程根据拷贝到内核态的监听集合, 轮询访问fd们(自动), 监听状态变化 (轮询范围根据select的最大文件描述符参数(第一个参数))
- 在一次轮询过程中, 发现有文件状态就绪, 把就绪状态文件描述符放回拷贝到内核态的监听集合中, 并触发select结束阻塞
- 把内核态的存储就绪的文件描述符集合, 拷贝回用户态
- select结束
有个问题,select一次应当只能检测到一个fd就绪,所以聊天的例子是靠循环去多次select,检测读就绪与标准输入就绪。
select的超时设置
struct timval{ long tv_sec;//秒 long tv_usec;.//微秒 };
- 可通过select返回值确定是超时返回(0)还是就绪返回fd数量(正数)。
- timeout(select最后一个参数,指向一个
timeval
结构体的指针)随着select阻塞时间递减,因为就绪时间导致select返回,则timeout为剩余时间,其实就是可以用timeout.tv_sec获取秒。
select poll epoll区别
select、poll和epoll都是用于实现 I/O 多路复用的方式,可以在同一时间内监听多个文件描述符的就绪状态。
select 是一种比较老的方式,它使用
位图
来表示文件描述符的状态。
- 调用 select 时,内核需要遍历整个位图,检查每个文件描述符是否就绪。这种轮询的方式在连接数量很少时还是很有效的,但当连接数量增多时,性能会下降。不适合海量监听, 少量就绪的情况
- 由于使用位图来保存描述符,所以 select 还有描述符个数的限制,一般只能支持 1024 个
- 不过 select 的跨平台性比较好,几乎所有的平台都可以支持。
poll 使用链表结构来表示文件描述符的状态,没有最大连接数的限制。和 select 函数一样,poll 返回后,需要轮询来获取就绪的描述符,因此随着监视的描述符数量的增长,其效率也会线性下降。
epoll 是 Linux 特有的一种方式,它使用了事件驱动的模型,没有最大连接数的限制。它将文件描述符添加到 epoll 的事件集合中,等待事件的发生。与 select 和 poll 不同的是,epoll 不需要轮询,它使用回调的方式,只关注真正发生事件的文件描述符。这使得epoll在大规模高并发连接下具有卓越的性能。
应用场景上,select和poll适用于连接数量较少的场景,而epoll则适用于需要处理大规模并发连接且性能要求较高的场景。