1. 程式人生 > >IO的多路複用和訊號驅動

IO的多路複用和訊號驅動

  Linux為多路複用IO提供了較多的介面,有select(),pselect(),poll()的方式,繼承自BSD和System V 兩大派系。

  select模型比較簡單,“輪詢”檢測fd_set的狀態,然後再採取相應的措施。

  訊號驅動模型有必要仔細研究一下,一般有如下步驟:

  1. 設定安裝函式,訊號是驅動訊號是SIGIO(最好使用sigaction的方式,方便設定flag為SA_RESTART,因為client中讀取終端的syscall可能會被中斷,有必要重啟。當然,使用signal()的方式然後再對errno進行判斷是否為ETNTR,自行重啟也是一種方法。但是signal()的可移植性問題,我強烈不建議使用)
  2. 設定fd的屬主。F_SETOWN,要接受訊號的程序,fcntl().
  3. 設定fd的非同步標誌。小細節,用'|'新增,要把之前的狀態保留,也就是先F_GETFL再F_SETFL。(注意:在開啟檔案open()時設定標識O_ASYNC無實質效果)  

    On Linux, specifying the O_ASYNC
    flag when calling open() has no effect. To enable signal-driven I/O, we must
    instead set this flag using the fcntl() F_SETFL operation (Section 5.3).————《the linux programming interface》 4.3.1

  4. sigaction()安裝

  具體進下文client例程。

  寫了一個聊天程式的demo,把這兩種技術都使用了。服務端採取多路複用的IO方式,代替多進(線)程的模型,客服端採取的是訊號驅動,如下:

容易產生bug的地方都寫註釋裡邊了。

serv.c

  1 #include <sys/select.h>
  2 #include <sys/socket.h>
  3 #include <netinet/in.h>
  4 #include <arpa/inet.h>
  5 #include <unistd.h>
  6
#include <fcntl.h> 7 #include <stdlib.h> 8 #include <string.h> 9 #include <stdio.h> 10 #include <signal.h> 11 12 void endServ(int sig) 13 { 14 printf("Server ended!\n"); 15 exit(-1); 16 } 17 18 int main() 19 { 20 // signal 21 struct sigaction act; 22 sigemptyset(&act.sa_mask); 23 act.sa_handler = endServ; 24 act.sa_flags = 0; 25 sigaction(SIGINT,&act,0); 26 printf("This server is started,enter CTRL+C to end. \n"); 27 28 int servfd = socket(AF_INET,SOCK_STREAM,0); 29 if(servfd == -1) { 30 printf("something wrong with socket():%m\n"); 31 exit(-1); 32 } 33 34 struct sockaddr_in addr; 35 addr.sin_family = AF_INET; 36 addr.sin_port = htons(9999); 37 inet_pton(AF_INET,"127.0.0.1",&addr.sin_addr); 38 39 if (bind(servfd,(struct sockaddr*)&addr,sizeof(addr)) == -1) { 40 printf("Some thing wrong with bind():%m\n"); 41 exit(-1); 42 } 43 printf("Bind success!\n"); 44 45 listen(servfd,20); 46 47 int numbers=0;//how many clients has accepted 48 fd_set fs; 49 FD_ZERO(&fs); 50 int client_fd[100]; 51 int i; 52 for(i=0;i<100;++i) 53 { 54 client_fd[i] = -1; 55 } 56 57 int maxfd=0; 58 char buf[1024]; 59 for(;;) 60 { 61 maxfd =0; 62 FD_ZERO(&fs); 63 FD_SET(servfd,&fs); 64 maxfd = maxfd>servfd?maxfd:servfd; 65 for(i=0;i<numbers;++i) 66 { 67 if(client_fd[i] != -1) { 68 FD_SET(client_fd[i],&fs); 69 maxfd = maxfd>client_fd[i]?maxfd:client_fd[i]; 70 } 71 } 72 73 int res = select(maxfd+1,&fs,0,0,0); 74 if(res == -1) { 75 printf("Something wrong with select():%m\n"); 76 exit(-1); 77 } 78 79 if(FD_ISSET(servfd,&fs) && numbers < 100) { 80 printf("New client!\n"); 81 client_fd[numbers] = accept(servfd,0,0); 82 numbers++; 83 } 84 85 for(i=0;i<numbers;++i) 86 { 87 bzero(buf,sizeof(buf)); 88 //judge if client_fd[i] equal -1 is necessary 89 //if a client quited,next time the program will 90 //have a segment default 91 //also it should be in the front. 92 if(client_fd[i] != -1 && FD_ISSET(client_fd[i],&fs)) 93 { 94 res = recv(client_fd[i],buf,sizeof(buf),0); 95 if(res == 0) { 96 printf("A client quit\n"); 97 close(client_fd[i]); 98 FD_CLR(client_fd[i],&fs); 99 client_fd[i] = -1; 100 } 101 else if(res == -1) { 102 printf("Something wrong with net.\n"); 103 exit(-1); 104 } 105 else { 106 int j; 107 for(j=0;j<numbers;++j) 108 { 109 if(client_fd[j] != -1) 110 send(client_fd[j],buf,sizeof(buf),0); 111 } 112 } 113 } 114 } 115 } 116 }

client:

 1 #include <signal.h>
 2 #include <unistd.h>
 3 #include <sys/socket.h>
 4 #include <fcntl.h>
 5 #include <arpa/inet.h>
 6 #include <netinet/in.h>
 7 #include <stdio.h>
 8 #include <string.h>
 9 #include <stdlib.h>
10 #include <errno.h>
11 
12 static int fd;
13 
14 void show(int sig)
15 {
16     char buf[1024] = {0};
17     int n = read(fd,buf,sizeof(buf));
18     buf[n] = 0;
19     write(1,"MSG:",strlen("MSG:"));
20     write(1,buf,strlen(buf));
21 }
22 
23 int main()
24 {
25     struct sigaction act;
26     sigemptyset(&act.sa_mask);
27     act.sa_handler = show;
28     //This is necessary,in last loop read() counld be interrupt;
29     act.sa_flags = SA_RESTART;
30     sigaction(SIGIO,&act,0);
31 
32     fd = socket(AF_INET,SOCK_STREAM,0);
33     if(fd == -1) {
34         printf("Cannot get socketfd!:%m\n");
35         exit(-1);
36     }
37 
38     struct sockaddr_in addr;
39     addr.sin_family = AF_INET;
40     addr.sin_port = htons(9999);
41     inet_pton(AF_INET,"127.0.0.1",&addr.sin_addr);
42 
43     if(connect(fd,(struct sockaddr*)&addr,sizeof(addr)) != -1)
44         printf("connect success!\n");
45     else
46         exit(-1);
47 
48     int flag = fcntl(fd,F_GETFL);
49     flag |= O_ASYNC;
50     fcntl(fd,F_SETFL,flag);
51     fcntl(fd,F_SETOWN,getpid());
52 
53     char buffer[1024]={0};
54     for(;;)
55     {
56         int n = read(0,buffer,sizeof(buffer));
57         if(n==-1)
58             break;
59         send(fd,buffer,n,0);
60     }
61 
62     write(1,"Closed.",strlen("Closed."));
63 }