任務:php
(1)實現服務器與客戶端間的通訊。html
(2)能夠實現HTTP請求中的GET方法。web
(3)提供靜態網頁瀏覽功能,如可瀏覽:HTML頁面,無格式文本,常見圖像格式等。服務器
(4)提供能夠傳遞參數的動態網頁瀏覽功能。網絡
(5)能夠檢查一些明顯錯誤報告給客戶端,如:403無權訪問,404找不到所請求的文件,501不支持相應方法等。app
(6)在服務器端可輸出HTTP響應的相關信息。socket
服務器端可配置參數,如:主目錄,首頁文件名,HTTP端口號等項。ide
套接字接口函數
套接字接口是一組函數,它們和Unix I/O函數結合起來,用以建立網絡網絡應用。this
客戶端和服務器使用socket函數來建立一個套接字描述符。
服務器端經過bind函數告訴內核將addr中的服務器套接字地址和套接字描述符sockfd聯繫起來;經過listen函數告訴內核,描述符是被服務器而不是客戶端使用;經過accept函數來等待來自客戶端的鏈接請求。
HTTP(超文本傳輸協議)被用在Web客戶端和服務器之間的交互。當客戶端須要服務時,它就向服務器發送一個HTTP請求說明須要的東西,服務器收到後解析,而後進行迴應。本次實驗,實現了應用最普遍的GET方法,並經過解析,判斷是須要動態內容,仍是靜態內容,仍是錯誤處理。
doit函數:處理一個HTTP事務。
首先讀和解析請求行,得到方法,若是不是所支持的GET方法,就發送給它一個錯誤消息,並返回到主程序,主程序隨後關閉鏈接並等待下一個鏈接請求。不然,讀而且調用read_requesthdrs忽略報頭中的其餘信息。而後將URI解析爲一個文件名和一個可能爲空的CGI參數字符串,而且設置一個標誌,代表請求的是靜態內容仍是動態內容。若是所需的文件不存在,就發送一個錯誤信息到客戶端並返回。
parse_uri函數:解析URI並轉爲所需的文件名。
初始時設主目錄爲當前目錄,可執行文件的目錄爲./cgi-bin。若是請求的是衣一個靜態內容,就清楚CGI參數字符串,而後轉爲文件的路徑名。若是請求的是動態內容,就提取出全部的CGI參數,而後轉爲文件的路徑名。
serve_static函數:處理靜態內容。
首先打開filename文件,用mmap函數將文件映射到一個虛擬內存空間,而後關閉這個文件,rio_writen函數複製到客戶端已鏈接的描述符。這樣前段就能夠顯示出所需的內容。
serve_dynamic函數:處理動態內容。
首先向客戶端發送成功相應的內容,而後派生一個子進程,子進程用來自請求URI的CGI參數初始化QUERY_STRING環境變量,重定向標準輸出到已鏈接文件描述符,而後執行CGI程序。
signal_r(SIGCHLD, sigchild_handler)函數:處理殭屍進程函數。
fork建立出不少進程,這些進程執行完之後就exit(0)而後發個信號通知主進程,exit函數退出後,進程的有關資源(打開的文件描述符,棧,寄存器,有關信息等)尚未釋放掉,若是進程不被回收的話就會佔用存儲器資源這樣的進程就稱爲殭屍進程。因此解決辦法就是主進程使用一個信號處理函數,等待殭屍進程回收。
1 /* 2 * @filename: webServer.c 3 * @author: Flyuz 4 * @date: 2018年6月25日 5 * @description: 主程序 6 */ 7 8 #include "functionLib.h" 9 10 void doit(int fd); 11 void serve_static(int fd, char *filename, int filesize); 12 int parse_uri(char *uri, char *filename, char *cgiargs); 13 void read_requesthdrs(rio_t *rp); 14 void clienterror(int fd, char *cause, char *errnum, char *shortmsg, 15 char *longmsg); 16 void get_filetype(char *filename, char *filetype); 17 void serve_dynamic(int fd, char *filename, char *cgiargs); 18 19 20 int main(int argc, char **argv) { 21 int listenfd, connfd; 22 socklen_t clientlen; 23 24 struct sockaddr_in clientaddr; 25 26 if (argc != 2) { 27 fprintf(stderr, "Usage: %s <port>\n", argv[0]); 28 exit(1); 29 } 30 31 printf("The web server has been started.....\n"); 32 33 listenfd = Open_listenfd(argv[1]); 34 /* 35 信號處理函數 36 用來處理殭屍進程 37 */ 38 signal_r(SIGCHLD, sigchild_handler); 39 40 while (1) { 41 clientlen = sizeof(clientaddr); 42 if((connfd = Accept(listenfd, (SA *)&clientaddr, &clientlen)) < 0) 43 { 44 if(errno == EINTR) 45 continue; 46 else 47 printf("Accept error..."); 48 } 49 pid_t pid = Fork(); 50 if(pid == 0) 51 { 52 doit(connfd); 53 Close(connfd); 54 exit(0); 55 } 56 else 57 { 58 Close(connfd); 59 } 60 } 61 } 62 63 void doit(int fd) { 64 int is_static; 65 struct stat sbuf; 66 rio_t rio; 67 char buf[MAXLINE], method[MAXLINE], uri[MAXLINE], version[MAXLINE]; 68 char filename[MAXLINE]; //設置根目錄 69 char cgiargs[MAXLINE]; 70 71 //初始化 rio 結構 72 Rio_readinitb(&rio, fd); 73 //讀取http請求行 74 Rio_readlineb(&rio, buf, MAXLINE); 75 //格式化存入 把該行拆分 76 sscanf(buf, "%s %s %s", method, uri, version); 77 78 //只能處理GET請求,若是不是GET請求的話返回錯誤 79 if (strcasecmp(method, "GET")) { 80 clienterror(fd, method, "501", "Not Implemented", 81 "Flyuz does not implement thid method"); 82 return; 83 } 84 85 //讀取並忽略請求報頭 86 read_requesthdrs(&rio); 87 88 // memset(filename,0,sizeof(filename)); 89 //解析 URI 90 is_static = parse_uri(uri, filename, cgiargs); 91 92 //文件不存在 93 if (stat(filename, &sbuf) < 0) { 94 clienterror(fd, filename, "404", "Not found", 95 "Flyuz couldn't find this file"); 96 return; 97 } 98 99 if (is_static) { //服務靜態內容 100 if (!(S_ISREG(sbuf.st_mode)) || !(S_IRUSR & sbuf.st_mode)) { 101 clienterror(fd, filename, "403", "Forbidden", 102 "Flyuz couldn't read the file"); 103 return; 104 } 105 serve_static(fd, filename, sbuf.st_size); 106 } else { //服務動態內容 107 if (!(S_ISREG(sbuf.st_mode)) || !(S_IXUSR & sbuf.st_mode)) { 108 clienterror(fd, filename, "403", "Forbidden", 109 "Flyuz couldn't run the CGI program"); 110 return; 111 } 112 serve_dynamic(fd, filename, cgiargs); 113 } 114 } 115 116 /* 117 * 讀取http 請求報頭,沒法使用請求報頭的任何信息,讀取以後忽略掉 118 */ 119 void read_requesthdrs(rio_t *rp) { 120 char buf[MAXLINE]; 121 122 Rio_readlineb(rp, buf, MAXLINE); 123 printf("%s", buf); 124 //空文本行終止請求報頭,碰到 空行 就結束 空行後面是內容實體 125 while (strcmp(buf, "\r\n")) { 126 Rio_readlineb(rp, buf, MAXLINE); 127 printf("%s", buf); 128 } 129 return; 130 } 131 132 /* 133 * 解析URI 爲 filename 和 CGI 參數 134 * 若是是動態內容返回0;靜態內容返回 1 135 */ 136 int parse_uri(char *uri, char *filename, char *cgiargs) { 137 if (!strstr(uri, 138 "cgi-bin")) { //默承認執行文件都放在cgi-bin下,這裏表示沒有找到 139 strcpy(cgiargs, ""); 140 strcpy(filename, "."); 141 strcat(filename, uri); 142 /* 143 if(uri[strlen(uri)-1] == "/") //設置默認文件 144 strcat(filename, "index.html"); 145 */ 146 147 return 1; // static 148 } else { //動態內容 149 char *ptr = strchr(uri, '?'); 150 if (ptr) { //有參數 151 strcpy(cgiargs, ptr + 1); 152 *ptr = '\0'; 153 } else { //無參數 154 strcpy(cgiargs, ""); 155 } 156 157 strcpy(filename, "."); 158 strcat(filename, uri); 159 return 0; 160 } 161 } 162 163 /* 164 * 功能:發送一個HTTP響應,主體包含一個本地文件的內容 165 */ 166 void serve_static(int fd, char *filename, int filesize) { 167 int srcfd; 168 char *srcp, body[MAXBUF], filetype[MAXLINE]; 169 170 /* 發送 響應行 和 響應報頭 */ 171 get_filetype(filename, filetype); 172 173 sprintf(body, "HTTP/1.0 200 OK\r\n"); 174 sprintf(body, "%sServer: Flyuz Web Server\r\n", body); 175 sprintf(body, "%sConnection:close\r\n", body); 176 sprintf(body, "%sContent-length: %d\r\n", body, filesize); 177 sprintf(body, "%sContent-type: %s\r\n\r\n", body, filetype); 178 Rio_writen(fd, body, strlen(body)); 179 printf("Response headers: \n%s", body); 180 181 /* 發送響應主體 即請求文件的內容 */ 182 /* 只讀方式發開filename文件,獲得描述符*/ 183 srcfd = Open(filename, O_RDONLY, 0); 184 /* 將srcfd 的前 filesize 個字節映射到一個從地址 srcp 開始的只讀虛擬存儲器區域 185 * 返回被映射區的指針 */ 186 srcp = Mmap(0, filesize, PROT_READ, MAP_PRIVATE, srcfd, 0); 187 /* 此後經過指針 srcp 操做,不須要這個描述符,因此關掉 */ 188 Close(srcfd); 189 190 Rio_writen(fd, srcp, filesize); 191 /* 釋放映射的虛擬存儲器區域 */ 192 Munmap(srcp, filesize); 193 } 194 195 /* 196 * 功能:從文件名得出文件的類型 197 */ 198 void get_filetype(char *filename, char *filetype) { 199 if (strstr(filename, ".html") || strstr(filename, ".php")) 200 strcpy(filetype, "text/html"); 201 else if (strstr(filename, ".gif")) 202 strcpy(filetype, "image/gif"); 203 else if (strstr(filename, ".png")) 204 strcpy(filetype, "image/png"); 205 else if (strstr(filename, ".ipg")) 206 strcpy(filetype, "image/jpeg"); 207 else 208 strcpy(filetype, "text/plain"); 209 } 210 211 /* 212 * 功能:運行客戶端請求的CGI程序 213 */ 214 void serve_dynamic(int fd, char *filename, char *cgiargs) { 215 char buf[MAXLINE]; 216 char *emptylist[] = {NULL}; 217 218 /* 發送響應行 和 響應報頭 */ 219 sprintf(buf, "HTTP/1.0 200 OK\r\n"); 220 Rio_writen(fd, buf, strlen(buf)); 221 sprintf(buf, "Server Flyuz Web Server\r\n"); 222 Rio_writen(fd, buf, strlen(buf)); 223 224 /* 剩下的內容由CGI程序負責發送 */ 225 if (Fork() == 0) { //子進程 226 setenv("QUERY_STRING", cgiargs, 1); 227 Dup2(fd, STDOUT_FILENO); 228 Execve(filename, emptylist, __environ); 229 } 230 Wait(NULL); 231 /* 232 if(strstr(filename, ".php")) { 233 sprintf(response, "HTTP/1.1 200 OK\r\n"); 234 sprintf(response, "%sServer: Pengge Web Server\r\n",response); 235 sprintf(response, "%sConnection: close\r\n",response); 236 sprintf(response, "%sContent-type: %s\r\n\r\n",response,filetype); 237 Write(connfd, response, strlen(response)); 238 printf("Response headers:\n"); 239 printf("%s\n",response); 240 php_cgi(filename, connfd,cgi_params); 241 Close(connfd); 242 exit(0); 243 //靜態頁面輸出 244 } 245 */ 246 } 247 248 /* 249 * 檢查一些明顯的錯誤,報告給客戶端 250 */ 251 void clienterror(int fd, char *cause, char *errnum, char *shortmsg, 252 char *longmsg) { 253 char buf[MAXLINE], body[MAXBUF]; 254 255 /* 構建HTTP response 響應主體 */ 256 sprintf(body, "<html><title>Flyuz Error</title>"); 257 sprintf(body, 258 "%s<body bgcolor=" 259 "white" 260 ">\r\n", 261 body); 262 sprintf(body, "%s<center><h1>%s: %s</h1></center>", body, errnum, shortmsg); 263 sprintf(body, "%s<center><h3>%s: %s</h3></center>", body, longmsg, cause); 264 sprintf(body, "%s<hr><center>The Flyuzy Web server</center>\r\n", body); 265 266 /* 打印HTTP響應報文 */ 267 sprintf(buf, "HTTP/1.0 %s %s", errnum, shortmsg); 268 Rio_writen(fd, buf, strlen(buf)); 269 sprintf(buf, "Content-type: text/html\r\n"); 270 Rio_writen(fd, buf, strlen(buf)); 271 sprintf(buf, "Content-length: %d\r\n\r\n", (int)strlen(body)); 272 Rio_writen(fd, buf, strlen(buf)); 273 Rio_writen(fd, body, strlen(body)); 274 }
1 /* 2 * @filename: functionLib.h 3 * @author: Flyuz 4 * @date: 2018年6月25日 5 * @description: 函數實現 6 */ 7 8 #ifndef ALL_H 9 #define ALL_H 10 11 #include <stdio.h> 12 #include <string.h> 13 #include <stdlib.h> 14 #include <unistd.h> 15 #include <ctype.h> 16 #include <netinet/in.h> 17 #include <arpa/inet.h> 18 #include <sys/socket.h> 19 #include <sys/types.h> 20 #include <sys/wait.h> 21 #include <errno.h> 22 #include <fcntl.h> 23 #include <signal.h> 24 #include <sys/stat.h> 25 #include <sys/mman.h> 26 27 #define RIO_BUFSIZE 8192 28 #define MAXLINE 8192 /* 一行最大長度 */ 29 typedef struct { 30 int rio_fd; //內部讀緩衝區描述符 31 int rio_cnt; //內部緩衝區中未讀字節數 32 char *rio_bufp; //內部緩衝區中下一個未讀的字節 33 char rio_buf[RIO_BUFSIZE]; //內部讀緩衝區 34 }rio_t; //一個類型爲rio_t的讀緩衝區 35 36 #define MAXLINE 8192 37 #define MAXBUF 8192 38 #define LISTENQ 1024 39 40 typedef struct sockaddr SA; 41 typedef void handler_t(int); 42 void unix_error(char *msg); 43 44 /* Process control wrappers */ 45 pid_t Fork(); 46 void Execve(const char *filename, char *const argv[], char *const envp[]); 47 pid_t Wait(int *status); 48 49 /* Unix I/O */ 50 int Open(const char *pathname, int flags, mode_t mode); 51 void Close(int fd); 52 int Dup2(int oldfd, int newfd); 53 54 55 56 void *Mmap(void *addr, size_t len, int prot, int flags, int fd, off_t offset); 57 void Munmap(void *start, size_t length); 58 59 /* RIO package */ 60 ssize_t rio_readn(int fd, void *usrbuf, size_t n); 61 ssize_t rio_writen(int fd, void *usrbuf, size_t n); 62 void rio_readinitb(rio_t *rp, int fd); 63 ssize_t rio_readlineb(rio_t *rp, void *usrbuf, size_t maxlen); 64 ssize_t rio_readnb(rio_t *rp, void *usrbuf, size_t n); 65 66 67 ssize_t Rio_readn(int fd, void *usrbuf, size_t n); 68 void Rio_writen(int fd, void *usrbuf, size_t n); 69 void Rio_readinitb(rio_t *rp, int fd); 70 ssize_t Rio_readlineb(rio_t *rp, void *usrbuf, size_t maxlen); 71 ssize_t Rio_readnb(rio_t *rp, void *usrbuf, size_t n); 72 73 74 int open_listenfd(char *port); 75 76 int Open_listenfd(char *port); 77 78 int Accept(int s, struct sockaddr *addr, socklen_t *addrlen); 79 80 handler_t *signal_r(int signum, handler_t *handler); 81 void sigchild_handler(int sig); 82 83 #endif
1 /* 2 * @filename: functionLib.c 3 * @author: Flyuz 4 * @date: 2018年6月25日 5 * @description: 輔助函數 6 */ 7 8 #include "functionLib.h" 9 10 void unix_error(char *msg) 11 { 12 fprintf(stderr, "%s: %s\n",msg, strerror(errno)); 13 exit(0); 14 } 15 16 pid_t Fork() 17 { 18 pid_t pid; 19 pid = fork(); 20 if(pid < 0) 21 unix_error("fork error"); 22 return pid; 23 } 24 25 void Execve(const char *filename, char *const argv[], char *const envp[]) 26 { 27 if(execve(filename, argv,envp) < 0) 28 unix_error("Execve error"); 29 } 30 31 pid_t Wait(int *status) 32 { 33 pid_t pid; 34 35 if((pid = wait(status)) < 0) 36 unix_error("Wait error"); 37 return pid; 38 } 39 40 41 42 int Open(const char *pathname, int flags, mode_t mode) 43 { 44 int rc; 45 46 if((rc = open(pathname, flags, mode)) < 0) 47 unix_error("Open error"); 48 return rc; 49 } 50 51 int Accept(int s, struct sockaddr *addr, socklen_t *addrlen) 52 { 53 int rc; 54 55 if((rc = accept(s, addr,addrlen)) < 0) 56 unix_error("Accept error"); 57 return rc; 58 } 59 60 void Close(int fd) 61 { 62 int rc; 63 64 if((rc = close(fd)) < 0) 65 unix_error("Close error"); 66 } 67 68 int Dup2(int oldfd, int newfd) 69 { 70 int rc; 71 72 if((rc = dup2(oldfd, newfd)) < 0 ) 73 unix_error("Dup2 error"); 74 return rc; 75 } 76 77 78 void *Mmap(void *addr, size_t len, int prot, int flags, int fd, off_t offset) 79 { 80 void *ptr; 81 82 if((ptr = mmap(addr, len, prot, flags, fd, offset)) == ((void *)-1)) 83 unix_error("Mmap error"); 84 return ptr; 85 } 86 87 void Munmap(void *start, size_t length) 88 { 89 if(munmap(start, length) < 0) 90 unix_error("Munmap error"); 91 } 92 /* 93 * 無緩衝輸入函數 94 * 成功返回收入的字節數 95 * 若EOF返回0 ,出錯返回-1 96 */ 97 ssize_t rio_readn(int fd, void *usrbuf, size_t n) 98 { 99 char *bufp = usrbuf; 100 size_t nleft = n; 101 ssize_t nread; 102 while(nleft > 0) { 103 if((nread = read(fd,bufp,nleft)) < 0) { 104 if(errno == EINTR) 105 nread = 0; 106 else 107 return -1; 108 } else if( nread == 0 ) 109 break; 110 nleft -= nread; 111 bufp += nread; 112 } 113 return (n-nleft); 114 } 115 /* 116 * 無緩衝輸出函數 117 * 成功返回輸出的字節數,出錯返回-1 118 */ 119 ssize_t rio_writen(int fd, void *usrbuf, size_t n) 120 { 121 char *bufp = usrbuf; 122 size_t nleft = n; 123 ssize_t nwritten; 124 while(nleft > 0) { 125 if((nwritten = write(fd, bufp, nleft)) <= 0) { 126 if(errno == EINTR) 127 nwritten = 0; 128 else 129 return -1; 130 } 131 nleft -= nwritten; 132 bufp += nwritten; 133 } 134 return n; 135 } 136 137 138 static ssize_t rio_read(rio_t *rp, char *usrbuf, size_t n) 139 { 140 while(rp -> rio_cnt <= 0) { 141 rp -> rio_cnt = read(rp -> rio_fd, rp -> rio_buf, 142 sizeof(rp -> rio_buf)); 143 if(rp -> rio_cnt < 0) { 144 if(errno != EINTR) 145 return -1; 146 } else if(rp -> rio_cnt == 0) 147 return 0; 148 else 149 rp -> rio_bufp = rp -> rio_buf; 150 } 151 152 int cnt = rp -> rio_cnt < n ? rp -> rio_cnt : n; 153 154 memcpy(usrbuf, rp -> rio_bufp, cnt); 155 rp -> rio_bufp += cnt; 156 rp -> rio_cnt -= cnt; 157 return cnt; 158 } 159 // 初始化rio_t結構,建立一個空的讀緩衝區 160 // 將fd和地址rp處的這個讀緩衝區聯繫起來 161 void rio_readinitb(rio_t *rp, int fd) 162 { 163 rp -> rio_fd = fd; 164 rp -> rio_cnt = 0; 165 rp -> rio_bufp = rp -> rio_buf; 166 } 167 168 //帶緩衝輸入函數 169 ssize_t rio_readnb(rio_t *rp, void *usrbuf, size_t n) 170 { 171 size_t nleft = n; 172 ssize_t nread; 173 char *bufp = usrbuf; 174 175 while(nleft > 0) { 176 if((nread = rio_read(rp, bufp, nleft)) < 0) 177 return -1; 178 else if (nread == 0) 179 break; 180 181 nleft -= nread; 182 bufp += nread; 183 } 184 return (n-nleft); 185 } 186 /* 187 **帶緩衝輸入函數,每次輸入一行 188 **從文件rp讀出一個文本行(包括結尾的換行符),將它拷貝到usrbuf,而且用空字符來結束這個文本行 189 **最多讀maxlen-1個字節,餘下的一個留給結尾的空字符 190 **/ 191 ssize_t rio_readlineb(rio_t *rp, void *usrbuf, size_t maxlen) 192 { 193 int n, rc; 194 char c, *bufp = usrbuf; 195 for(n = 1; n<maxlen; n++) { 196 if((rc = rio_read(rp, &c, 1)) == 1) { 197 *bufp++ = c; 198 if( c == '\n' ){ 199 n++; 200 break; 201 } 202 } else if(rc == 0) { 203 if( n == 1 ) 204 return 0; 205 else 206 break; 207 } else 208 return -1; 209 } 210 *bufp = '\0'; 211 return n-1; 212 } 213 214 ssize_t Rio_readn(int fd, void *usrbuf, size_t n) 215 { 216 ssize_t nbytes; 217 if((nbytes = rio_readn(fd, usrbuf, n)) < 0) 218 unix_error("Rio_readn error"); 219 220 return nbytes; 221 } 222 223 void Rio_writen(int fd, void *usrbuf, size_t n) 224 { 225 if(rio_writen(fd, usrbuf, n) != n) 226 unix_error("Rio_writen error"); 227 } 228 229 void Rio_readinitb(rio_t *rp, int fd) 230 { 231 rio_readinitb(rp, fd); 232 } 233 234 ssize_t Rio_readlineb(rio_t *rp, void *usrbuf, size_t maxlen) 235 { 236 ssize_t nbytes; 237 if((nbytes = rio_readlineb(rp, usrbuf, maxlen)) < 0) 238 unix_error("Rio_readlineb error"); 239 return nbytes; 240 } 241 242 ssize_t Rio_readnb(rio_t *rp, void *usrbuf, size_t n) 243 { 244 ssize_t nbytes; 245 if((nbytes = rio_readnb(rp, usrbuf, n)) < 0) 246 unix_error("Rio_readnb error"); 247 return nbytes; 248 } 249 250 /*打開並返回監聽描述符*/ 251 int open_listenfd(char *port) 252 { 253 int listenfd, optval = 1; 254 struct sockaddr_in serveraddr; 255 256 if((listenfd = socket(AF_INET, SOCK_STREAM, 0)) < 0){ 257 return -1; 258 } 259 260 if(setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, 261 (const void *)&optval, sizeof(int)) < 0) 262 return -1; 263 264 bzero((char *)&serveraddr, sizeof(serveraddr)); 265 266 serveraddr.sin_family = AF_INET; 267 serveraddr.sin_addr.s_addr = htonl(INADDR_ANY); 268 serveraddr.sin_port = htons((unsigned short)atoi(port)); 269 270 if(bind(listenfd, (SA *)&serveraddr, sizeof(serveraddr)) < 0) 271 return -1; 272 273 if(listen(listenfd, LISTENQ) < 0) 274 return -1; 275 276 return listenfd; 277 } 278 279 int Open_listenfd(char *port) 280 { 281 int rc; 282 if((rc = open_listenfd(port)) < 0) 283 unix_error("open_listenfd error"); 284 return rc; 285 } 286 //信號處理 287 handler_t *signal_r(int signum, handler_t *handler) 288 { 289 struct sigaction action, old_action; 290 291 action.sa_handler = handler; 292 sigemptyset(&action.sa_mask); 293 action.sa_flags = SA_RESTART; 294 295 if (sigaction(signum, &action, &old_action) < 0) 296 perror("Signal error"); 297 return (old_action.sa_handler); 298 } 299 //排隊 防止阻塞 300 void sigchild_handler(int sig) 301 { 302 int stat; 303 while(waitpid(-1,&stat,WNOHANG)>0); 304 return; 305 }