一区二区三区在线-一区二区三区亚洲视频-一区二区三区亚洲-一区二区三区午夜-一区二区三区四区在线视频-一区二区三区四区在线免费观看

服務(wù)器之家:專注于服務(wù)器技術(shù)及軟件下載分享
分類導(dǎo)航

Linux|Centos|Ubuntu|系統(tǒng)進(jìn)程|Fedora|注冊(cè)表|Bios|Solaris|Windows7|Windows10|Windows11|windows server|

服務(wù)器之家 - 服務(wù)器系統(tǒng) - Linux - IO多路復(fù)用之poll全面總結(jié)(必看篇)

IO多路復(fù)用之poll全面總結(jié)(必看篇)

2021-12-13 20:59Linux教程網(wǎng) Linux

下面小編就為大家?guī)?lái)一篇IO多路復(fù)用之poll全面總結(jié)(必看篇)。小編覺(jué)得挺不錯(cuò)的。現(xiàn)在就分享給大家。也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧

1、基本知識(shí)

poll的機(jī)制與select類似,與select在本質(zhì)上沒(méi)有多大差別,管理多個(gè)描述符也是進(jìn)行輪詢,根據(jù)描述符的狀態(tài)進(jìn)行處理,但是poll沒(méi)有最大文件描述符數(shù)量的限制。poll和select同樣存在一個(gè)缺點(diǎn)就是,包含大量文件描述符的數(shù)組被整體復(fù)制于用戶態(tài)和內(nèi)核的地址空間之間,而不論這些文件描述符是否就緒,它的開(kāi)銷隨著文件描述符數(shù)量的增加而線性增大。

2、poll函數(shù)

函數(shù)格式如下所示:

# include <poll.h>
int poll ( struct pollfd * fds, unsigned int nfds, int timeout);

pollfd結(jié)構(gòu)體定義如下:

?
1
2
3
4
5
6
struct pollfd {
 
int fd;     /* 文件描述符 */
short events;     /* 等待的事件 */
short revents;    /* 實(shí)際發(fā)生了的事件 */
} ;

 

每一個(gè)pollfd結(jié)構(gòu)體指定了一個(gè)被監(jiān)視的文件描述符,可以傳遞多個(gè)結(jié)構(gòu)體,指示poll()監(jiān)視多個(gè)文件描述符。每個(gè)結(jié)構(gòu)體的events域是監(jiān)視該文件描述符的事件掩碼,由用戶來(lái)設(shè)置這個(gè)域。revents域是文件描述符的操作結(jié)果事件掩碼,內(nèi)核在調(diào)用返回時(shí)設(shè)置這個(gè)域。events域中請(qǐng)求的任何事件都可能在revents域中返回。合法的事件如下:

pollin 有數(shù)據(jù)可讀。

pollrdnorm   有普通數(shù)據(jù)可讀。

pollrdband  有優(yōu)先數(shù)據(jù)可讀。

pollpri 有緊迫數(shù)據(jù)可讀。

pollout      寫數(shù)據(jù)不會(huì)導(dǎo)致阻塞。

pollwrnorm   寫普通數(shù)據(jù)不會(huì)導(dǎo)致阻塞。

pollwrband    寫優(yōu)先數(shù)據(jù)不會(huì)導(dǎo)致阻塞。

pollmsgsigpoll 消息可用。

此外,revents域中還可能返回下列事件:

poller   指定的文件描述符發(fā)生錯(cuò)誤。

pollhup 指定的文件描述符掛起事件。

pollnval指定的文件描述符非法。

這些事件在events域中無(wú)意義,因?yàn)樗鼈冊(cè)诤线m的時(shí)候總是會(huì)從revents中返回。

使用poll()和select()不一樣,你不需要顯式地請(qǐng)求異常情況報(bào)告。

pollin | pollpri等價(jià)于select()的讀事件,pollout |pollwrband等價(jià)于select()的寫事件。pollin等價(jià)于pollrdnorm |pollrdband,而pollout則等價(jià)于pollwrnorm。例如,要同時(shí)監(jiān)視一個(gè)文件描述符是否可讀和可寫,我們可以設(shè)置 events為pollin |pollout。在poll返回時(shí),我們可以檢查revents中的標(biāo)志,對(duì)應(yīng)于文件描述符請(qǐng)求的events結(jié)構(gòu)體。如果pollin事件被設(shè)置,則文件描述符可以被讀取而不阻塞。如果pollout被設(shè)置,則文件描述符可以寫入而不導(dǎo)致阻塞。這些標(biāo)志并不是互斥的:它們可能被同時(shí)設(shè)置,表示這個(gè)文件描述符的讀取和寫入操作都會(huì)正常返回而不阻塞。

timeout參數(shù)指定等待的毫秒數(shù),無(wú)論i/o是否準(zhǔn)備好,poll都會(huì)返回。timeout指定為負(fù)數(shù)值表示無(wú)限超時(shí),使poll()一直掛起直到一個(gè)指定事件發(fā)生;timeout為0指示poll調(diào)用立即返回并列出準(zhǔn)備好i/o的文件描述符,但并不等待其它的事件。這種情況下,poll()就像它的名字那樣,一旦選舉出來(lái),立即返回。

返回值和錯(cuò)誤代碼

成功時(shí),poll()返回結(jié)構(gòu)體中revents域不為0的文件描述符個(gè)數(shù);如果在超時(shí)前沒(méi)有任何事件發(fā)生,poll()返回0;失敗時(shí),poll()返回-1,并設(shè)置errno為下列值之一:
ebadf       一個(gè)或多個(gè)結(jié)構(gòu)體中指定的文件描述符無(wú)效。

efaultfds 指針指向的地址超出進(jìn)程的地址空間。

eintr  請(qǐng)求的事件之前產(chǎn)生一個(gè)信號(hào),調(diào)用可以重新發(fā)起。

einvalnfds參數(shù)超出plimit_nofile值。

enomem     可用內(nèi)存不足,無(wú)法完成請(qǐng)求。

3、測(cè)出程序

編寫一個(gè)echo server程序,功能是客戶端向服務(wù)器發(fā)送信息,服務(wù)器接收輸出并原樣發(fā)送回給客戶端,客戶端接收到輸出到終端。

服務(wù)器端程序如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
 
#include <netinet/in.h>
#include <sys/socket.h>
#include <poll.h>
#include <unistd.h>
#include <sys/types.h>
 
#define ipaddress  "127.0.0.1"
#define port    8787
#define maxline   1024
#define listenq   5
#define open_max  1000
#define inftim   -1
 
//函數(shù)聲明
//創(chuàng)建套接字并進(jìn)行綁定
static int socket_bind(const char* ip,int port);
//io多路復(fù)用poll
static void do_poll(int listenfd);
//處理多個(gè)連接
static void handle_connection(struct pollfd *connfds,int num);
 
int main(int argc,char *argv[])
{
  int listenfd,connfd,sockfd;
  struct sockaddr_in cliaddr;
  socklen_t cliaddrlen;
  listenfd = socket_bind(ipaddress,port);
  listen(listenfd,listenq);
  do_poll(listenfd);
  return 0;
}
 
static int socket_bind(const char* ip,int port)
{
  int listenfd;
  struct sockaddr_in servaddr;
  listenfd = socket(af_inet,sock_stream,0);
  if (listenfd == -1)
  {
    perror("socket error:");
    exit(1);
  }
  bzero(&servaddr,sizeof(servaddr));
  servaddr.sin_family = af_inet;
  inet_pton(af_inet,ip,&servaddr.sin_addr);
  servaddr.sin_port = htons(port);
  if (bind(listenfd,(struct sockaddr*)&servaddr,sizeof(servaddr)) == -1)
  {
    perror("bind error: ");
    exit(1);
  }
  return listenfd;
}
 
static void do_poll(int listenfd)
{
  int connfd,sockfd;
  struct sockaddr_in cliaddr;
  socklen_t cliaddrlen;
  struct pollfd clientfds[open_max];
  int maxi;
  int i;
  int nready;
  //添加監(jiān)聽(tīng)描述符
  clientfds[0].fd = listenfd;
  clientfds[0].events = pollin;
  //初始化客戶連接描述符
  for (i = 1;i < open_max;i++)
    clientfds[i].fd = -1;
  maxi = 0;
  //循環(huán)處理
  for ( ; ; )
  {
    //獲取可用描述符的個(gè)數(shù)
    nready = poll(clientfds,maxi+1,inftim);
    if (nready == -1)
    {
      perror("poll error:");
      exit(1);
    }
    //測(cè)試監(jiān)聽(tīng)描述符是否準(zhǔn)備好
    if (clientfds[0].revents & pollin)
    {
      cliaddrlen = sizeof(cliaddr);
      //接受新的連接
      if ((connfd = accept(listenfd,(struct sockaddr*)&cliaddr,&cliaddrlen)) == -1)
      {
        if (errno == eintr)
          continue;
        else
        {
          perror("accept error:");
          exit(1);
        }
      }
      fprintf(stdout,"accept a new client: %s:%d\n", inet_ntoa(cliaddr.sin_addr),cliaddr.sin_port);
      //將新的連接描述符添加到數(shù)組中
      for (i = 1;i < open_max;i++)
      {
        if (clientfds[i].fd < 0)
        {
          clientfds[i].fd = connfd;
          break;
        }
      }
      if (i == open_max)
      {
        fprintf(stderr,"too many clients.\n");
        exit(1);
      }
      //將新的描述符添加到讀描述符集合中
      clientfds[i].events = pollin;
      //記錄客戶連接套接字的個(gè)數(shù)
      maxi = (i > maxi ? i : maxi);
      if (--nready <= 0)
        continue;
    }
    //處理客戶連接
    handle_connection(clientfds,maxi);
  }
}
 
static void handle_connection(struct pollfd *connfds,int num)
{
  int i,n;
  char buf[maxline];
  memset(buf,0,maxline);
  for (i = 1;i <= num;i++)
  {
    if (connfds[i].fd < 0)
      continue;
    //測(cè)試客戶描述符是否準(zhǔn)備好
    if (connfds[i].revents & pollin)
    {
      //接收客戶端發(fā)送的信息
      n = read(connfds[i].fd,buf,maxline);
      if (n == 0)
      {
        close(connfds[i].fd);
        connfds[i].fd = -1;
        continue;
      }
      // printf("read msg is: ");
      write(stdout_fileno,buf,n);
      //向客戶端發(fā)送buf
      write(connfds[i].fd,buf,n);
    }
  }
}

客戶端代碼如下所示:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
#include <netinet/in.h>
#include <sys/socket.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <poll.h>
#include <time.h>
#include <unistd.h>
#include <sys/types.h>
 
#define maxline   1024
#define ipaddress  "127.0.0.1"
#define serv_port  8787
 
#define max(a,b) (a > b) ? a : b
 
static void handle_connection(int sockfd);
 
int main(int argc,char *argv[])
{
  int         sockfd;
  struct sockaddr_in servaddr;
  sockfd = socket(af_inet,sock_stream,0);
  bzero(&servaddr,sizeof(servaddr));
  servaddr.sin_family = af_inet;
  servaddr.sin_port = htons(serv_port);
  inet_pton(af_inet,ipaddress,&servaddr.sin_addr);
  connect(sockfd,(struct sockaddr*)&servaddr,sizeof(servaddr));
  //處理連接描述符
  handle_connection(sockfd);
  return 0;
}
 
static void handle_connection(int sockfd)
{
  char  sendline[maxline],recvline[maxline];
  int   maxfdp,stdineof;
  struct pollfd pfds[2];
  int n;
  //添加連接描述符
  pfds[0].fd = sockfd;
  pfds[0].events = pollin;
  //添加標(biāo)準(zhǔn)輸入描述符
  pfds[1].fd = stdin_fileno;
  pfds[1].events = pollin;
  for (; ;)
  {
    poll(pfds,2,-1);
    if (pfds[0].revents & pollin)
    {
      n = read(sockfd,recvline,maxline);
      if (n == 0)
      {
          fprintf(stderr,"client: server is closed.\n");
          close(sockfd);
      }
      write(stdout_fileno,recvline,n);
    }
    //測(cè)試標(biāo)準(zhǔn)輸入是否準(zhǔn)備好
    if (pfds[1].revents & pollin)
    {
      n = read(stdin_fileno,sendline,maxline);
      if (n == 0)
      {
        shutdown(sockfd,shut_wr);
    continue;
      }
      write(sockfd,sendline,n);
    }
  }
}

4、程序測(cè)試結(jié)果

IO多路復(fù)用之poll全面總結(jié)(必看篇)

IO多路復(fù)用之poll全面總結(jié)(必看篇)

IO多路復(fù)用之poll全面總結(jié)(必看篇)

以上就是小編為大家?guī)?lái)的io多路復(fù)用之poll全面總結(jié)(必看篇)全部?jī)?nèi)容了,希望大家多多支持服務(wù)器之家~

延伸 · 閱讀

精彩推薦
主站蜘蛛池模板: 国产99精品免费视频看6 | 超逼网 | 日本在线不卡免 | 娇女的呻吟亲女禁忌h16 | 成免费视频 | 性啪啪chinese东北女人 | 亚洲狠狠综合久久 | 热99精品只有里视频最新 | 日韩日日操 | 午夜伦伦电影理论片大片 | 久久青青草视频在线观 | 青青草国产一区二区三区 | 欧美精品久久久久久久免费观看 | 天堂成人在线观看 | 亚洲社区在线 | 欧美极品摘花过程 | juliaann大战两个黑人 | 美女在尿口隐私视频 | 男人搡女人视频免费看 | 手机在线伦理片 | 桃色导航 | 午夜一区二区免费视频 | 禁欲天堂| 99久久久无码国产精品 | 精品国产午夜久久久久九九 | 美女扒开腿让男人桶爽动态图片 | 国产免费好大好硬视频 | 初尝黑人巨大h文 | 小鸟酱喷水 | 国士李风起全文在线阅读 | 四虎成人影院网址 | 国产精品网站在线观看 | 亚洲精品一区二区三区中文字幕 | 成人精品视频 成人影院 | 2022日韩理论片在线观看 | 日韩小视频在线观看 | 国产福利你懂的 | 亚州成人 | 水多多凹凸福利视频导航 | 国产高清亚洲 | 大学生情侣在线 |