您的位置:首页 > 运维架构 > Linux

Linux select和poll系统调用

2015-08-11 17:50 603 查看
很多时候,我们调用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
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: