Linux select和poll系统调用

来源:互联网 发布:淘宝网售后服务网址 编辑:程序博客网 时间:2024/05/18 00:14
很多时候,我们调用read函数从文件描述符中读取数据时,如果此时恰好没有数据可读,read系统调用势必会阻塞住。同样,当调用write函数时,而没有空间供我们写入,write系统调用也会被阻塞住,直到有空间被写入时。那么在这种情况下我们为了不阻塞我们的程序,就需要检查文件描述符是否可读或可写。

1. select系统调用
首先我们来看一段代码:
#include <stdio.h>#include <unistd.h>int main(void){        int bytes_read;        char buffer[128];                bytes_read = read(STDIN_FILENO, buffer, sizeof(buffer));        if (buffer[bytes_read - 1] == '\n')                buffer[bytes_read - 1] = '\0';        printf("%s\n", buffer);        return 0;}

上面这段代码是从标准输入端读取字符并显示出来,如果我们不输入任何字符,那么read函数是会被阻塞的,直到我们输入了任意字符并按了回车键。那么如何避免被阻塞呢,来看select系统调用,原型如下:

#include <sys/select.h>int select(int nfds, fd_set *readfds, fd_set *writefds,           fd_set *exceptfds, struct timeval *timeout);
参数readfds、writefds、exceptfds都是指向文件描述符的指针,数据类型为fd_set。而readfds是用来检测输入的,writefds是用来检测输出的,exceptfds使用检测是否异常的。有关fd_set通常有四个宏供我们操作:FD_ZERO、FD_SET、FD_CLR、FD_ISSET。
void FD_CLR(int fd, fd_set *set);int  FD_ISSET(int fd, fd_set *set);void FD_SET(int fd, fd_set *set);void FD_ZERO(fd_set *set);
FD_ZERO是将fd_set所指向的集合清空,FD_SET是将文件描述符fd添加到fd_set所指向的集合中,FD_CLR是将文件描述符fd从fd_set所指向的集合中移除,而如果文件描述符fd是fd_set所指向的集合中的成员,则FD_ISSET返回true,否则返回false。文件描述符集合有一个最大容量限制,由常量FD_SETSIZE来决定,在Linux上,该常量值为1024。
参数nfds必须设定为比3个文件描述符集合中的所包含的最大文件描述符还要大1,什么意思呢,假如我们只关注read,而read集合中包含两个文件描述符,它们的值分别为2、3,那么这里nfds就必须设置为大于等于3+1。
参数timeout为超时时间。

使用select系统调用的示例如下:
#include <stdio.h>#include <unistd.h>#include <sys/select.h>int main(void){        int bytes_read, ready;        char buffer[128];        fd_set readfds;        struct timeval timeout;        FD_ZERO(&readfds);        FD_SET(STDIN_FILENO, &readfds);        timeout.tv_sec = 10;        timeout.tv_usec = 0;        ready = select(STDIN_FILENO+1, &readfds, NULL, NULL, &timeout);        if (ready) {                bytes_read = read(STDIN_FILENO, buffer, sizeof(buffer));                if (buffer[bytes_read - 1] == '\n')                        buffer[bytes_read - 1] = '\0';                printf("%s\n", buffer);        } else {                printf("No data to read\n");        }        return 0;}
如果没有数据可读,select系统调用也是会被阻塞的,直到超时为止,这里设定是的10秒。如果没有指定timeout参数,则select系统调用会永远阻塞下去。

2. poll系统调用
poll系统调用同select系统调用类似,原型如下:
#include <poll.h>int poll(struct pollfd *fds, nfds_t nfds, int timeout);
struct pollfd定义如下:
struct pollfd {    int   fd;         /* file descriptor */    short events;     /* requested events */    short revents;    /* returned events */};
参数nfds为fds元素个数。timeout为超时时间,单位为毫秒,如果timeout等于-1,poll系统调用会一直阻塞,如果timeout等于0,则poll系统调用之检测一次并立即退出,如果timeout大于0,则poll系统调用会最多阻塞timeout毫秒,直到数据可读或可写。

上面的代码使用poll系统调用修改如下:
#include <stdio.h>#include <unistd.h>#include <poll.h>int main(void){        int bytes_read, ready;        char buffer[128];        struct pollfd readfds;        readfds.fd = STDIN_FILENO;        readfds.events = POLLIN;        ready = poll(&readfds, 1, 10000);        if (ready) {                bytes_read = read(STDIN_FILENO, buffer, sizeof(buffer));                if (buffer[bytes_read - 1] == '\n')                        buffer[bytes_read - 1] = '\0';                printf("%s\n", buffer);        } else {                printf("No data to read\n");        }        return 0;}

参考教程:The Linux Programming Interface - A Linux and UNIX System Programming Handbook.pdf

0 0
原创粉丝点击