I/O复用(一)--select & poll

楔子

while ((n = read(STDIN_FILENO, buf, BUFSIZE)) > 0 )
   if (write(STDOUT_FILENO, buf ,n) != n )
       err_sys("write error")

上面这段代码很简单:从标准输入读入数据输出至标准输出。这里涉及两个描述符,这段代码的顺利完成要求两个描述符都准备好,只要其一没有准备好程序就无法进行下去,I/O复用就是用来处理这种问题的方法之一,涉及到的函数主要为: select poll & epoll。下面一一总结。

select函数

UNIX环境高级编程(第二版)声明如下:
返回:若有就绪的描述符则返回其数目,超时返回0,出错返回-1
#include<sys/select.h>

int select(int maxfdp1, fd_set *restrict readfds, fd_set *restrict writefds, fd_set *restrict exceptfds, struct timeval *restrict tvptr);
restrict是C99标准的关键字,表示只能通过着一个引用进行修改内存的值,用于编译器优化。

tvptr表示超时时间,结构体timeval声明为
struct timeval {
   long tv_sec;                    /* 秒 */
   long tv_usec;                 /* 微秒 */
};
这个参数有三种可能:
  • NULL   表示永远等待,仅当至少有一个描述符准备好时才返回
  • 0            表示不等待,查询描述符后立刻返回,即为轮询
  • >0          表示等待指定的时间内,至少有一个描述符准备好时才返回,否则超时
中间的三个参数readset, writeset, exceptset表示让select去查询的读描述符集合,写描述符集合以及异常描述符集合。
如何给这个三个描述符集指定一个或多个描述符呢?select通常使用一个整型数组,每个整数中的每一位对应一个描述符

fd_set可以进行的操作为
void FD_ZERO(fd_set *fdset)                                                        //   将fdset置0
void FD_SET(int fd, fd_set *fdset)                                               //     指定一个描述符
void FD_CLR(int fd, fd_set *fdset)                                              //     清空一个描述符 
void FD_ISSET(int fd, fd_set *fdset)                                           //     判断一个藐视符是否准备好

maxfdp1为所有描述符中值最大的+1,表示select会检测的描述符范围

下面是一个简单的应用:共有两个进程,通过管道进行通讯,在子进程中select对管道进行查询,管道准备好就读数据,然后输出至标准输出
#include <sys/select.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/termios.h>
#include <sys/ioctl.h>


#include <stdio.h>
#include <stdlib.h>
#include <stddef.h>
#include <string.h>
#include <unistd.h>
#include <signal.h>

#define MAXLINE 4096

int main(void)
{
	int		n;
	int		fd[2];
	pid_t	pid;
	char	line[MAXLINE];

	if (pipe(fd) < 0)
    printf("pipe error\n");
	if ((pid = fork()) < 0) {
    printf("fork error\n");
	} else if (pid > 0) {		/* parent */
		sleep(3);
    close(fd[0]);
		write(fd[1], "hello world\n", 12);
	} else {				/* child */
		struct timeval tv;
    tv.tv_sec = 0;
    tv.tv_usec = 0;
    close(fd[1]);
    fd_set readset;
    while (1) {
      FD_ZERO(&readset);
      FD_SET(fd[0], &readset);
      switch (select(fd[0]+1, &readset, NULL, NULL, &tv)>0) {
        case -1 : printf("error\n");exit(-1);break;
        case  0 : printf("wait\n");sleep(1);break;
        default :
          if (FD_ISSET(fd[0], &readset)) {
		        n = read(fd[0], line, MAXLINE);
		        write(STDOUT_FILENO, line, n);
            return ;
          }
          break;
      }
    }
	}
	exit(1);
}

poll函数

UNIX环境高级编程(第二版)声明如下:
返回:若有就绪的描述符则返回其数目,超时返回0,出错返回-1
#include <poll.h>

int poll(struct pollfd fdarray[], nfds_t nfds, int timeout);
与select不用,poll不是为每个状态构造一个描述符集,而是构造一个polldf结构数组,每个数组元素指定一个描述符以及所关心的状态
struct pollfd{
   int fd;
   short events;
   short revents;
};

fdarray数组长度由参数nfds进行说明,参数timeout与select的超时参数含义相同,单位为毫秒

events为描述符关心的状态,revents为poll查询后该描述符的状态,状态如下表

上面的例子通过poll修改后如下所示
#include <poll.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/termios.h>
#include <sys/ioctl.h>


#include <stdio.h>
#include <stdlib.h>
#include <stddef.h>
#include <string.h>
#include <unistd.h>
#include <signal.h>

#define MAXLINE 4096

int main(void)
{
	int		n;
	int		fd[2];
	pid_t	pid;
	char	line[MAXLINE];

	if (pipe(fd) < 0)
    printf("pipe error\n");
	if ((pid = fork()) < 0) {
    printf("fork error\n");
	} else if (pid > 0) {		/* parent */
		sleep(3);
    close(fd[0]);
		write(fd[1], "hello world\n", 12);
	} else {				/* child */
    close(fd[1]);
    struct pollfd fdarray[10];
    fdarray[0].fd = fd[0];
    fdarray[0].events = POLLRDNORM;
    while (1) {
      switch (poll(fdarray, 1, 0)>0) {
        case -1 : printf("error\n");exit(-1);break;
        case  0 : printf("wait\n");sleep(1);break;
        default :
          if ((fdarray[0].revents & POLLRDNORM) == POLLRDNORM) {
		        n = read(fdarray[0].fd, line, MAXLINE);
		        write(STDOUT_FILENO, line, n);
            return ;
          }
          break;
      }
    }
	}
	exit(1);
}






  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值