来源:蜘蛛抓取(WebSpider)
时间:2017-01-08 19:26
标签:
linux poll 阻塞
2639人阅读
Linux设备驱动入门(19)
& & & & 多路复用一般用于I/O操作可能会被阻塞的情况,对可能会有阻塞的I/O的管道、网路进行编程。下面我们来看个例子来说明如何使用两个多路复用函数。
& & & & 本实例中主要实现通过调用select()函数来监听3个终端的输入(分别重定向到两个管道文件的虚拟终端以及主程序所运行的虚拟终端),并分别进行相应的处理。通过监视主程序的虚拟终端标准输入来实现程序的控制(例如:程序结束);以两个管道作为数据的输入,主程序将从两个管道读取输入的字符串写入到标准输出文件(屏幕)。
& & & & 为了充分表现select()调用的功能,在运行主程序的时候,需要打开3个虚拟终端:首先用mknod命令创建来个管道in1和in2。接下来在两个虚拟终端中分别运行cat &in1 和cat & in2。同时在第三个虚拟终端上运行主程序。
程序设计流程图:
程序设计multiplex_select.c代码:
* multiplex_select.c
Created on:
Author: liwei.cai
#include &fcntl.h&
#include &stdio.h&
#include &unistd.h&
#include &stdlib.h&
#include &time.h&
#include &errno.h&
#include &string.h&
#define MAX_BUFFER_SIZE 1024
//缓冲区的大小
#define IN_FILES
//多路复用输入文件数目
#define TIME_DELAY
// 超时秒数
#define MAX(a,b)
((a & b)?(a):(b))
int main()
int fds[IN_FILES];
char buf[MAX_BUFFER_SIZE];
int i, res, real_read,
fd_set inset, tmp_
//首先以只读非阻塞方式打开两个管道文件
fds[0] = 0;
if((fds[1] = open(&in1&, O_RDONLY | O_NONBLOCK)) & 0)
printf(&Open inl error!\n&);
if((fds[2] = open(&in2&, O_RDONLY | O_NONBLOCK)) & 0)
printf(&Open in2 error!\n&);
//取出两个文件描述符中的较大者
maxfd = MAX(MAX(fds[0],fds[1]),fds[2]);
//初始化读集合inset,并在读集合中加入相应的描述集
FD_ZERO(&inset);
for(i = 0; i & IN_FILES; i++)
FD_SET(fds[i],&inset);
FD_SET(0, &inset);
tv.tv_sec = TIME_DELAY;
tv.tv_usec = 0;
//循环测试该文件描述符是否准备就绪,并调用select函数对象相关文件描述符做对应操作
while(FD_ISSET(fds[0], &inset) || FD_ISSET(fds[1], &inset)
|| FD_ISSET(fds[2], &inset))
//文件描述符的备份,这样可以避免每次进行初始化
tmp_inset =
res = select(maxfd + 1, &tmp_inset, NULL,NULL, &tv);
switch(res)
printf(&Select error!\n&);
case 0: //超时
printf(&Time out !\n&);
for(i = 0; i & IN_FILES; i++)
if (FD_ISSET(fds[i], &tmp_inset))
memset(buf, 0, MAX_BUFFER_SIZE);
real_read = read(fds[i], buf, MAX_BUFFER_SIZE);
if(real_read & 0)
if(errno != EAGAIN)
else if (!real_read)
close(fds[i]);
FD_CLR(fds[i], &inset);
if (i == 0)
//主程序终端控制
if((buf[0] == 'q') || (buf[0] == 'Q'))
//显示管道输入字符串
buf[real_read] = '\0';
printf(&%s&, buf);
程序设计multiplex_poll.c代码:
* multiplex_poll.c
Created on:
Author: liwei.cai
#include &fcntl.h&
#include &stdio.h&
#include &unistd.h&
#include &stdlib.h&
#include &time.h&
#include &errno.h&
#include &poll.h&
#include &string.h&
#define MAX_BUFFER_SIZE 1024
//缓冲区的大小
#define IN_FILES
//多路复用输入文件数目
#define TIME_DELAY
// 超时秒数
#define MAX(a,b)
((a & b)?(a):(b))
int main(void)
struct pollfd fds[IN_FILES];
char buf[MAX_BUFFER_SIZE];
int i, res, real_read,
//首先按照一定的权限打开两个源文件
fds[0].fd = 0;
if((fds[1].fd = open(&in1&, O_RDONLY |O_NONBLOCK)) & 0)
printf(&Open in1 error!\n&);
if((fds[2].fd = open(&in2&, O_RDONLY | O_NONBLOCK)) & 0)
printf(&Open in2 error!\n&);
//取出两个文件描述符中的较大者
for(i = 0; i & IN_FILES; i++)
fds[i].events = POLLIN;
//循环测试该文件描述符是否准备就绪,并调用select函数对象相关文件描述符做对应操作
while(fds[0].events || fds[1].events
|| fds[2].events)
if(poll(fds, IN_FILES,0) & 0)
printf(&Poll error!\n&);
for (i = 0; i & IN_FILES; i++)
if (fds[i].revents)
memset(buf, 0, MAX_BUFFER_SIZE);
real_read = read (fds[i].fd, buf, MAX_BUFFER_SIZE);
if (real_read & 0)
if (errno != EAGAIN)
else if (!real_read)
close(fds[i].fd);
fds[i].events = 0;
if (i == 0)
if ((buf[0] == 'q') || (buf[0] == 'Q'))
buf[real_read] = '\0';
printf(&%s&, buf);
}// end of if real_read
} //end of if revents
} //end of for
} //end of while
两个函数的运行方式和结果差不多,请思考下,到底哪种复用函数的效率高一些?
参考知识库
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:102977次
积分:1345
积分:1345
排名:千里之外
原创:27篇
评论:25条
(1)(1)(1)(5)(7)(12)(1)(3)(1)(1) poll函数和select函数非常相似,但是函数接口不一样。
int poll(struct pollfd fdarray[], nfds_t nfds, int timeout);
int select(int maxfdp1, fd_set *restrict readfds, fd_set *restrict expectfds, struct timeval * restrict tvptr);
其中poll函数中,结构pollfd如下:
struct pollfd{
int fd; //file descriptor
short event;//event of interest on fd
short revent;//event that occurred on fd
每一个pollfd结构体指定了一个被监视的文件描述符,可以传递多个结构体,指示poll()监视多个文件描述符。每个结构体的events域是监视该文件描述符的事件掩码,由用户来设置这个域。revents域是文件描述符的操作结果事件掩码。内核在调用返回时设置这个域。events域中请求的任何事件都可能在revents域中返回。合法的事件如下:POLLIN有数据可读。POLLRDNORM有普通数据可读。POLLRDBAND有优先数据可读。POLLPRI有紧迫数据可读。POLLOUT写数据不会导致阻塞。POLLWRNORM写普通数据不会导致阻塞。POLLWRBAND写优先数据不会导致阻塞。POLLMSGSIGPOLL 消息可用。
此外,revents域中还可能返回下列事件:POLLER指定的文件描述符发生错误。POLLHUP指定的文件描述符挂起事件。POLLNVAL指定的文件描述符非法。
这些事件在events域中无意义,因为它们在合适的时候总是会从revents中返回。使用poll()和select()不一样,你不需要显式地请求异常情况报告。POLLIN | POLLPRI等价于select()的读事件,POLLOUT |POLLWRBAND等价于select()的写事件。POLLIN等价于POLLRDNORM |POLLRDBAND,而POLLOUT则等价于POLLWRNORM。例如,要同时监视一个文件描述符是否可读和可写,我们可以设置 events为POLLIN |POLLOUT。在poll返回时,我们可以检查revents中的标志,对应于文件描述符请求的events结构体。如果POLLIN事件被设置,则文件描述符可以被读取而不阻塞。如果POLLOUT被设置,则文件描述符可以写入而不导致阻塞。这些标志并不是互斥的:它们可能被同时设置,表示这个文件描述符的读取和写入操作都会正常返回而不阻塞。timeout参数指定等待的毫秒数,无论I/O是否准备好,poll都会返回。timeout指定为负数值表示无限超时;timeout为0指示poll调用立即返回并列出准备好I/O的文件描述符,但并不等待其它的事件。这种情况下,poll()就像它的名字那样,一旦选举出来,立即返回。
返回值和错误代码成功时,poll()返回结构体中revents域不为0的文件描述符个数;如果在超时前没有任何事件发生,poll()返回0;失败时,poll()返回-1,并设置errno为下列值之一:EBADF一个或多个结构体中指定的文件描述符无效。EFAULTfds指针指向的地址超出进程的地址空间。EINTR请求的事件之前产生一个信号,调用可以重新发起。EINVALnfds参数超出PLIMIT_NOFILE值。ENOMEM可用内存不足,无法完成请求。
阅读(...) 评论()C语言:poll函数...........
我的图书馆
C语言:poll函数...........
poll函数起源于SVR3,最初局限于流设备。SVR4取消了这种限制,允许poll工作在任何描述字上。poll提供的功能与select类似,不过在处理流设备时,它能够提供额外的信息。
1.#include &poll.h&
3.int poll(struct pollfd *fdarray, unsigned long nfds, int timeout);
4.&&&&&&&&&&&&&& 返回:就绪描述字的个数,0-超时,-1-出错
第一个参数是指向一个结构数组第一个元素的指针。每个数组元素都是一个pollfd结构,用于指定某个给定描述字fd的条件。
struct pollfd{
&&&&&&&&&&&&& //descriptor to check
&&& //events of interest on fd
&& //events that occurred on fd
要的条件由events成员指定,而返回的结果则在revents中。常用条件及含意说明如下:
poll函数可用的测试值
普通或优先级带数据可读
POLLRDNORM
普通数据可读
POLLRDBAND
优先级带数据可读
高优先级数据可读
普通数据可写
POLLWRNORM
普通数据可写
POLLWRBAND
优先级带数据可写
描述字不是一个打开的文件
注意:后三个只能作为描述字的返回结果在revents中,而不能作为测试条件用于events中。
第二个参数nfds是用来指定数组fdarray的长度。
最后一个参数timeout是指定poll函数返回前等待多长时间。它的取值如下:
立即返回,不阻塞进程
等待指定数目的毫秒数
一个使用poll的网络程序例子:
001./**
002.& *TCP回射的服务端程序
003.& */
004.#include &stdio.h&
005.#include &stdlib.h&
006.#include &unistd.h&
007.#include &sys/socket.h&
008.#include &sys/types.h&
009.#include &netinet/in.h&
010.#include &netdb.h&
011.#include &string.h&
012.#include &errno.h&
013.#include &poll.h&&& //for poll
015.#define LISTENQ 1024
016.#define MAXLINE 1024
017.#define OPEN_MAX 50000
018.#define SERVER_PORT 3333
020.#ifndef INFTIM&&&& /*按照书上解释:POSIX规范要求INFTIM在头文件&poll.h&中定义,不过*/
021.#define INFTIM -1& /*许多系统仍然把它定义在头文件&sys/stropts.h&中,但是经过我的测试*/
022.#endif&&&&&&&&&&&& /*即使都包含这两个文件,编译器也找不到,不知何解。索性自己定义了。*/
024.int main(int argc, char *argv[])
026.&&& int i, maxi, listenfd, connfd,
027.&&&
028.&&& ssize_
029.&&& socklen_
030.&&& struct sockaddr_in servaddr,
031.&&& struct hostent& *
032.&&& char buf[BUFSIZ];
033.&&& struct pollfd client[OPEN_MAX]; /*用于poll函数第一个参数的数组*/
035.&&& if( argc != 2 )
036.&&& {
037.&&&&&&& printf("Please input %s &hostname&\n", argv[0]);
038.&&&&&&& exit(1);
039.&&& }
041.&&& //创建socket
042.&&& if( (listenfd = socket(AF_INET, SOCK_STREAM,0)) & 0 )
043.&&& {
044.&&&&&&& printf("Create socket error!\n");
045.&&&&&&& exit(1);
046.&&& }
048.&&& //设置地址结构
049.&&& bzero(&servaddr, sizeof(servaddr));
050.&&& servaddr.sin_family = AF_INET;
051.&&& if( (hp = gethostbyname(argv[1])) != NULL )
052.&&& {
053.&&&&&&& bcopy(hp-&h_addr, (struct sockaddr*)&servaddr.sin_addr, hp-&h_length);
054.&&& }
055.&&& else if(inet_aton(argv[1], &servaddr.sin_addr) & 0 )
056.&&& {
057.&&&&&&& printf("Input Server IP error!\n");
058.&&&&&&& exit(1);
059.&&& }
060.&&& servaddr.sin_port = htons(SERVER_PORT);
062.&&& //绑定地址
063.&&& if( bind(listenfd, (struct sockaddr*)&servaddr, sizeof(servaddr)) & 0 )
064.&&& {
065.&&&&&&& printf("IPaddress bound failure!\n");
066.&&&&&&& exit(1);
067.&&& }
069.&&& //开始监听
070.&&& listen(listenfd, LISTENQ);
072.&&& client[0].fd =&&&&&&&& /*将数组中的第一个元素设置成监听描述字*/
074.&&& client[0].events = POLLIN;& /*将测试条件设置成普通或优先级带数据可读,此处书中为POLLRDNORM,
075.&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& 但是怎么也编译不过去 ,编译器就是找不到,所以就临时改成了POLLIN这个条件,
076.&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& 希望以后能弄清楚。
077.&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& */
079.&&& for(i = 1;i & OPEN_MAX; ++i)&&&& /*数组中的其它元素将暂时设置成不可用*/
080.&&&&&&& client[i].fd = -1;
081.&&& maxi = 0;
083.&&& while(1)
084.&&& {
085.&&&&&&& nready = poll(client, maxi+1,INFTIM); //将进程阻塞在poll上
086.&&&&&&& if( client[0].revents & POLLIN/*POLLRDNORM*/ ) /*先测试监听描述字*/
087.&&&&&&& {
088.&&&&&&&&&&& connfd = accept(listenfd,(struct sockaddr*)&servaddr, &clilen);
089.&&&&&&&&&&& for(i = 1; i & OPEN_MAX; ++i)
090.&&&&&&&&&&&&&&& if( client[i].fd & 0 )
091.&&&&&&&&&&&&&&& {
092.&&&&&&&&&&&&&&&&&&& client[i].fd =& /*将新连接加入到测试数组中*/
093.&&&&&&&&&&&&&&&&&&& client[i].events = POLLIN;//POLLRDNORM; /*测试条件普通数据可读*/
094.&&&&&&&&&&&&&&&&&&&
095.&&&&&&&&&&&&&&& }
096.&&&&&&&&&&& if( i == OPEN_MAX )
097.&&&&&&&&&&& {
098.&&&&&&&&&&&&&&& printf("too many clients"); //连接的客户端太多了,都达到最大值了
099.&&&&&&&&&&&&&&& exit(1);
100.&&&&&&&&&&& }
102.&&&&&&&&&&& if( i & maxi )
103.&&&&&&&&&&&&&&& maxi =& //maxi记录的是数组元素的个数
105.&&&&&&&&&&& if( --nready &= 0 )
106.&&&&&&&&&&&&&&&&& //如果没有可读的描述符了,就重新监听连接
107.&&&&&&& }
109.&&&&&&& for(i = 1; i &= i++)& /*测试除监听描述字以后的其它连接描述字*/
110.&&&&&&& {
111.&&&&&&&&&&& if( (sockfd = client[i].fd) & 0) /*如果当前描述字不可用,就测试下一个*/
112.&&&&&&&&&&&&&&&
114.&&&&&&&&&&& if(client[i].revents & (POLLIN/*POLLRDNORM*/ | POLLERR))/*如果当前描述字返回的是普通数据可读或出错条件*/
115.&&&&&&&&&&& {
116.&&&&&&&&&&&&&&& if( (n = read(sockfd, buf, MAXLINE)) & 0) //从套接口中读数据
117.&&&&&&&&&&&&&&& {
118.&&&&&&&&&&&&&&&&&&& if( errno == ECONNRESET) //如果连接断开,就关闭连接,并设当前描述符不可用
119.&&&&&&&&&&&&&&&&&&& {
120.&&&&&&&&&&&&&&&&&&&&&&& close(sockfd);
121.&&&&&&&&&&&&&&&&&&&&&&& client[i].fd = -1;
122.&&&&&&&&&&&&&&&&&&& }
123.&&&&&&&&&&&&&&&&&&& else
124.&&&&&&&&&&&&&&&&&&&&&&& perror("read error");
125.&&&&&&&&&&&&&&& }
126.&&&&&&&&&&&&&&& else if(n == 0) //如果数据读取完毕,关闭连接,设置当前描述符不可用
127.&&&&&&&&&&&&&&& {
128.&&&&&&&&&&&&&&&&&&& close(sockfd);
129.&&&&&&&&&&&&&&&&&&& client[i].fd = -1;
130.&&&&&&&&&&&&&&& }
131.&&&&&&&&&&&&&&& else
132.&&&&&&&&&&&&&&&&&&& write(sockfd, buf, n); //打印数据
134.&&&&&&&&&&&&&&& if(--nready &= 0)
135.&&&&&&&&&&&&&&&&&&&
137.&&&&&&&&&&& }
138.&&&&&&& }
139.&&& }
141.&&& exit(0);
发表评论:
TA的最新馆藏