linux poll机制

本是古典 何须时尚 2022-08-10 03:45 282阅读 0赞

poll的是一种查询的方式,英文解释 :民意调查

poll提供的功能与select类似,不过在处理流设备时,它能够提供额外的信息。

函数原型:

  #include

  int poll(struct pollfd fd[], nfds_t nfds, int timeout);

参数:

fds为指向待查询的设备文件数组;

nfds描述第一个参数fds中有多少个设备;

timeout为查询不到我们期望的结果进程睡眠的时间;

返回值:查询到期望状态的设备文件个数

   1)第一个参数:一个结构数组,struct pollfd结构如下:

  

struct pollfd

{

  1. int fd; /\* 文件描述符 \*/ (待查询的设备)
  2. short events; /\* 等待的事件 \*/(待查询的设备的状态)
  3. short revents; /\* 实际发生了的事件 \*/
  4. \}

events和revents是通过对代表各种事件的标志进行逻辑或运算构建而成的。events包括要监视的事件,poll用已经发生的事件填充revents。poll函数通过在revents中设置标志肌肤POLLHUP、POLLERR和POLLNVAL来反映相关条件的存在。不需要在events中对于这些标志符相关的比特位进行设置。如果fd小于0, 则events字段被忽略,而revents被置为0.标准中没有说明如何处理文件结束。文件结束可以通过revents的标识符POLLHUN或返回0字节的常规读操作来传达。即使POLLIN或POLLRDNORM指出还有数据要读,POLLHUP也可能会被设置。因此,应该在错误检验之前处理正常的读操作。

poll函数的事件标志符值
















































常量 说明
POLLIN 普通或优先级带数据可读
POLLRDNORM 普通数据可读
POLLRDBAND 优先级带数据可读
POLLPRI 高优先级数据可读
POLLOUT 普通数据可写
POLLWRNORM 普通数据可写
POLLWRBAND 优先级带数据可写
POLLERR 发生错误
POLLHUP 发生挂起
POLLNVAL 描述字不是一个打开的文件

  注意:后三个只能作为描述字的返回结果存储在revents中,而不能作为测试条件用于events中。

2)第二个参数nfds:要监视的描述符的数目。

  3)最后一个参数timeout:是一个用毫秒表示的时间,是指定poll在返回前没有接收事件时应该等待的时间。如果 它的值为-1,poll就永远都不会超时。如果整数值为32个比特,那么最大的超时周期大约是30分钟。




















timeout值 说明
INFTIM 永远等待
0 立即返回,不阻塞进程
>0 等待指定数目的毫秒数

例子程序:

在/root/pro/fd1 /root/pro/fd2中分别有内容,

1234

5678

1122

3344

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include

#define BUFSIZE 1024

int main(int argc, char *argv[])
{
char buf[BUFSIZE];
int bytes;
struct pollfd *pollfd;
int i=0;
int nummonitor=0;
int numready;
int errno;
char *str;
if(argc != 3)
{
fprintf(stderr,”Usage:the argc num error\n”);
exit(1);
}

  1. if((pollfd = (struct pollfd\*)calloc(2, sizeof(struct pollfd))) == NULL) //为struct pollfd分配空间
  2. exit(1);
  3. for(i; i<2; i++) //初始化化struct pollfd结构
  4. \{
  5. str = (char\*)malloc(14\*sizeof(char));
  6. memcpy(str,"/root/pro/",14);
  7. strcat(str,argv\[i+1\]);//注意,需要把路劲信息放到str中,否则opne("/root/pro/argv\[i\]",O\_RDONLY)会出错
  8. printf("str=%s\\n",str);//原因在于,在” “之中的argv\[i\]是字符串,不会用变量代替argv\[i\].
  9. (pollfd+i)->fd = open(str,O\_RDONLY);
  10. if((pollfd+i)->fd >= 0)
  11. fprintf(stderr, "open (pollfd+%d)->fd:%s\\n", i, argv\[i+1\]);
  12. nummonitor++;
  13. (pollfd+i)->events = POLLIN;
  14. \}
  15. printf("nummonitor=%d\\n",nummonitor);
  16. while(nummonitor > 0)
  17. \{
  18. numready = poll(pollfd, 2, -1);
  19. if ((numready == -1) && (errno == EINTR))
  20. continue; //被信号中断,继续等待
  21. else if (numready == -1)
  22. break; //poll真正错误,推出
  23. printf("numready=%d\\n",numready);
  24. for (i=0;nummonitor>0 && numready>0; i++)
  25. \{
  26. if((pollfd+i)->revents & POLLIN)
  27. \{
  28. bytes = read(pollfd\[i\].fd, buf, BUFSIZE);
  29. numready--;
  30. printf("pollfd\[%d\]->fd read buf:\\n%s \\n", i, buf);
  31. nummonitor--;
  32. \}
  33. \}
  34. \}
  35. for(i=0; i<nummonitor; i++)
  36. close(pollfd\[i\].fd);
  37. free(pollfd);
  38. return 0;

}

输出结果:

1355754316_7071.jpg

二、poll应用举例

  1. int main(int argc, char **argv) { int fd; unsigned char key_val; int ret; struct pollfd fds[1];//查询数组的大小,这里我们仅查询一个设备文件 fd = open("/dev/buttons", O_RDWR); if (fd < 0) printf("can't open!\n"); fds[0].fd = fd;//查询的设备文件描述符为fd,也就是查询的设备是/dev/buttons fds[0].events = POLLIN;//查询事件是POLLIN,也就是/dev/buttons是否按下 while (1) { ret = poll(fds, 1, 5000);//查询的设备队列是fds,里面有1个设备,查询不到就睡眠5s,在睡眠中如果有期望状态出现也是可以返回 if (ret == 0) printf("time out\n"); //没有查询到按键按下,睡眠中也没有按键按下 else { read(fd, &key_val, 1); //查询到按键按下,读取这个按键的值 printf("key_val = 0x%x\n", key_val); } } return 0; }

三、poll内核实现过程(kernel-2.6.30.4)

(应用程序)poll->sys_poll->do_sys_poll->do_poll->do_pollfd->f_op->poll(驱动)

在do_poll中:

for (;;)

{ //逐个取出待查询数组中的每个文件描述符来查询

  1. for (; pfd != pfd\_end; pfd++)
  2. \{
  3. if (do\_pollfd(pfd, pt))
  4. \{
  5. count++;
  6. pt = NULL;
  7. \}
  8. \}
  9. pt = NULL;
  10. if (!count)//如果没有一个设备文件发生可读
  11. \{
  12. count = wait->error;
  13. if (signal\_pending(current))
  14. count = -EINTR;
  15. \}
  16. if (count || timed\_out)//超时或者有设备文件可读,程序直接返回
  17. break;
  18. if (end\_time && !to)
  19. \{
  20. expire = timespec\_to\_ktime(\*end\_time);
  21. to = &expire;
  22. \}
  23. //程序睡眠
  24. if (!poll\_schedule\_timeout(wait, TASK\_INTERRUPTIBLE, to, slack))
  25. timed\_out = 1;

}

在do_pollfd中:

if (file->f_op && file->f_op->poll)
mask = file->f_op->poll(file, pwait);//调用设备的poll函数,返回是否发生期望的设备状态

在f_op->poll中(针对按键驱动举例):

unsigned int mask = 0;
poll_wait(file, &button_waitq, wait); //将当前的设备加入到等待队列中,当它不是马上就休眠

if (ev_press)
mask |= POLLIN | POLLRDNORM;//返回设备文件现在的状态

return mask;

发表评论

表情:
评论列表 (有 0 条评论,282人围观)

还没有评论,来说两句吧...

相关阅读

    相关 Linux select函数和poll函数

    以前写服务器程序直接就都写成多线程的了,没考虑过其他方式,也没考虑到底哪种方式好; 前些日子看些人说windows下面用完成端口、Linux下面用epoll,这些效率高。