select pselect poll

来源:互联网 发布:checkbox js 控制选中 编辑:程序博客网 时间:2024/05/01 16:54

 

linux系统对于多路i/o转接提供了几个强大的函数,但是这些函数各有优缺点,参照网上的资料以及自己的测试,总结如下:

首先看一个程序的例子:

#include        <time.h>
#include        <stdio.h>
#include        <stdlib.h>
#include        <signal.h>
#include        <unistd.h>
#include        <sys/select.h>

#define BUFFSIZE 80

void err_sys(const char *p_error);

void sig_alrm(int signo)
{
    char s[] = "receive";

    psignal(signo, s);

    return;
}
int
main(int argc, char **argv)
{
    int             maxfdp1;
    fd_set          rset;
    sigset_t        sigmask;
    ssize_t         nread;
    char            buf[BUFFSIZE];
    int ret = 0;

    sigset_t sigset;
    struct sigaction act;

    // set SIGALRM signal handler
    act.sa_handler = sig_alrm;
    if (sigemptyset(&act.sa_mask) == -1)      
    err_sys("sigemptyset");
    act.sa_flags = 0;
    if (sigaction(SIGALRM, &act, NULL) == -1)
    err_sys("sigaction");

    // initialize signal set and addition SIGALRM into sigset
    if (sigemptyset(&sigset) == -1)
    err_sys("sigemptyet");
    if (sigaddset(&sigset, SIGALRM) == -1)
    err_sys("sigaddset");
    alarm(1);   

    FD_ZERO(&rset);
    FD_SET(STDIN_FILENO, &rset);
    maxfdp1 = STDIN_FILENO + 1;
    ret = select(maxfdp1, &rset, NULL, NULL, NULL);
//        ret = pselect(maxfdp1, &rset, NULL, NULL, NULL, &sigset);
    if (ret <= 0)
    {
        printf("%x %d/n",ret,ret);
            err_sys("pselect error");
    }

    if (FD_ISSET(STDIN_FILENO, &rset))
   {
            if ((nread = read(STDIN_FILENO, buf, BUFFSIZE)) == -1)
                    err_sys("read error");
            if (write(STDOUT_FILENO, buf, nread) != nread)
                    err_sys("write error");
    }

    exit(0);
}

void
err_sys(const char *p_error)
{
        perror(p_error);

        exit(1);
}

使用select函数的运行结果:
receive: Alarm clock
ffffffff -1
select error: Interrupted system call

使用pselect函数的运行结果:
如果用户不击打键盘,无任何输出,程序阻塞在pselect,键盘有输入时输出

receive: Alarm clock

pselect 正常返回,系统正常响应信号。

结论:

在select函数等待过程中,如果有信号产生,函数自动返回,并返回interrupt system call值 -1。
在pselect函数等待过程中,如果有信号产生,函数是否返回依赖最后一个参数的设定,当设置为空时,同select。

附一
网友文章,希望对读者理解有所帮助。

一、I/O多路转接(I/O multiplexing)

背景:当需要从多个描述符读时,应该怎么办?

先构造一张有关描述符的列表,然后调用一个函数,直到这些描述符中的一个准备好进行I/O时,该函数才返回。在返回时,它告诉进程哪些描述符已经准备好可以进行I/O

poll,pselect,select这三个函数使我们能够执行多路转接。

 

1selectpselect函数

 

(1) select函数

在所有依从POSIX的平台上,select函数使我们可以执行I/O多路转接。

#include <sys/select.h>

int select(int maxfdp1, fd_set *restrict readfds,fd_set *restrict writefds,

fd_set *restrict exceptfds, struct timeval *restrict tvptr);

Returns: count of ready descriptors, 0 on timeout, -1 on error

 

a.传向select的参数告诉内核什么?

1)我们所关心的描述符。

2)对于每个描述符,我们所关心的状态。(是否读一个给定的描述符?是否想写一个给定的描述符?是否关心一个描述符的异常状态?)。

3)愿意等待多长时间。(可以永远等待,等待一个固定时间量,或完全不等待)。

 

b.从select返回时,内核告诉我们什么?

1)已准备好的描述符的数量。

2)对于读、写或异常这三个状态中的每一个,哪些描述符已准备好。

使用这些返回信息,就可以调用相应的I/O函数(一般是readwrite),并且确知该函数不会被阻塞。

 

cselect的参数

struct timeval是愿意等待的时间。

struct timeval {
     long tv_sec;     /* seconds */
     long tv_usec;    /* and microseconds */
   };

tvptr==NULL:永远等待。如果捕捉到信号则终端此无限期等待。

tvptr->tv_sec==0&& tvptr->tv_usec==0:完全不等待。测试所有指定的描述符并立即返回。

tvptr->tv_sec!=0|| tvptr->tv_usec!=0:等待指定的秒数和微秒数。当指定的描述符之一准备好,或者指定的时间值已经超过时立即返回。

 

readfdswritefdsexceptfds指向描述符集的指针。分别为可读、可写、异常条件的各个描述符集。每个描述符集存放在一个FD_SET数据类型中。这种数据类型为每一可能的描述符保持了一位。

FD_SET数据类型可以进行的处理是:分配一个这种类型的变量;将这种类型的变量赋予同类型的另一个变量;或对于这种类型的变量使用下列4个函数中的一个:

#include <sys/select.h>
int FD_ISSET(int fd, fd_set *fdset);

Returns: nonzero if fd is in set, 0 otherwise

void FD_CLR(int fd, fd_set *fdset);

void FD_SET(int fd, fd_set *fdset);

void FD_ZERO(fd_set *fdset);

这些接口可以实现为宏或者函数。调用FD_CLR将指定fd_set变量位清除;调用FD_SET设置fd_set变量指定位;调用FD_ZEROfd_set变量所有位设置为0.

声明了一个描述符集后,必须用FD_ZERO清除其所有位,然后再其中设置我们关心的所有位。常用操作如下:

fd_set   rset;
int      fd;
FD_ZERO(&rset);
FD_SET(fd, &rset);
FD_SET(STDIN_FILENO, &rset);

 

select返回时,用FD_ISSET测试该集中的任何一个给定位是否仍旧被设置,如果被设这,则说明描述度准备好,可以进行操作了。

   if (FD_ISSET(fd, &rset)) {
       ...
   }

 

select的中间3个参数中的任意一个或者全部都可以是空指针,这表示相应状态并不关心。如果所有三个指针都是空指针,则select提供了较sleep更精确的计时器。

 

maxfdp1最大描述符+1。在三个描述符集中找出最大描述符编号值,然后+1。相应操作:

int fd1,fd2,fd3,maxfd,maxfd1;

if(fd1>fd2)

    {

       max_fd=fd1;

    }

    else

    {

       max_fd=fd2;

    }

if(max_fd<fd3)

       max_fd=fd3;

maxfd1=maxfd+1;

为什么要加1,是因为描述符编号是从0开始的。

 

dselect的返回值

返回-1表示出错。

返回0表示没有描述符准备好。

返回正值:表示已经准备好的描述符数。

 

(2) pselect函数

#include <sys/select.h>

int pselect(int maxfdp1, fd_set *restrict readfds, fd_set *restrict writefds,

fd_set*restrict exceptfds, const struct timespec *restrict tsptr, const sigset_t *restrict sigmask);

Returns: count of ready descriptors, 0 on timeout, 1 on error

pselectselect的不同之处:

1)select超时值用timeval指定,但pselecttimespec指定。timeval是秒和微妙,timespec是秒和纳秒所以timespec提供了比timeval更准确的超时时间。

2)pselect的超时值被声明为const,这就保证调用pselect不会改变此值。

3)对于pselect可以使用一个可选的信号屏蔽字。若sigmask为空,那么在与信号有关的方面,二者相同;若sigmask指向一信号屏蔽字,在调用pselect时,以原子操作的方式安装该信号屏蔽字。在返回时恢复以前的信号屏蔽字。

 

(3)示例:用select实现多路转接模块

  struct timeval wait_time;

  fd_set readfds;

  int max_fd,fd1,fd2,fd3,maxfd1;

  int res;

 

FD_ZERO(&readfds);

  FD_SET(fd1, &readfds);

  FD_SET(fd2, &readfds);

  FD_SET(fd3,&readfds);

 

  if(fd1>fd2)

    {

       max_fd=fd1;

    }

    else

    {

       max_fd=fd2;

    }

if(max_fd<fd3)

       max_fd=fd3;

maxfd1=maxfd+1;

 

    wait_time.tv_sec=0;

    wait_time.tv_usec=10000;

 

res=select(max_fd1, &readfds, NULL, NULL, &wait_time);

if(res==-1)

{printf(“select error/n”);

exit(1);

}

else if(res==0)

{printf(“no descriptor ready/n”);

exit(2);

}

 

if(FD_ISSET(fd1, &readfds))

{

….

}

 

if(FD_ISSET(fd2, &readfds))

{

….

}

 

if(FD_ISSET(fd3, &readfds))

{

….

}

 

2.poll函数

poll函数类似于select函数,但是程序员接口有所不同,另外,poll函数可以用于任何类型的文件描述符,但是它起源于系统V,所以pollSTREAMS系统紧密相关。

#include <poll.h>
int poll(struct pollfd fdarray[], nfds_t nfds, int timeout);

Returns: count of ready descriptors, 0 on timeout, -1 on error

apoll的参数

struct pollfd fdarray[]select不同,poll不为每个状态构造一个描述集,而是构造一个pollfd结构数组,每个数组元素指定一个描述符编号以及对其所关心的状态。

struct pollfd {
     int   fd;       /* file descriptor to check, or <0 to ignore */
     short events;   /* events of interest on fd */
     short revents;  /* events that occurred on fd */
   };

nfds说明fdarray数组中的元素数。

timeout超时时间。若timeout==-1,则永远等待;若timeout==0,则不等待;若timeout>0,则等待timeout毫秒。

bpoll的返回值

select同。



谢谢


wilson

总之:
再实际运用中
一般都是用select,好象poll是轮询,应该效率差些吧
主要是看代码写得怎么样,并不是这几个函数起主要原因
一般来说对于服务频率高但每次服务时间都不长的应用
用select效率是比较高的,但是对于服务次数较少,每次服务时间很长的
就用来说还是用多进程比较好

 

 

原创粉丝点击