精品欧美一区二区三区在线观看 _久久久久国色av免费观看性色_国产精品久久在线观看_亚洲第一综合网站_91精品又粗又猛又爽_小泽玛利亚一区二区免费_91亚洲精品国偷拍自产在线观看 _久久精品视频在线播放_美女精品久久久_欧美日韩国产成人在线

Linux下的進(jìn)程間通信:套接字和信號(hào)

系統(tǒng) Linux
篇是 Linux 下進(jìn)程間通信(IPC)系列的第三篇同時(shí)也是最后一篇文章。第一篇文章聚焦在通過(guò)共享存儲(chǔ)(文件和共享內(nèi)存段)來(lái)進(jìn)行 IPC,第二篇文章則通過(guò)管道(無(wú)名的或者命名的)及消息隊(duì)列來(lái)達(dá)到相同的目的。

[[267074]]

學(xué)習(xí)在 Linux 中進(jìn)程是如何與其他進(jìn)程進(jìn)行同步的。

本篇是 Linux 下進(jìn)程間通信(IPC)系列的第三篇同時(shí)也是最后一篇文章。第一篇文章聚焦在通過(guò)共享存儲(chǔ)(文件和共享內(nèi)存段)來(lái)進(jìn)行 IPC,第二篇文章則通過(guò)管道(無(wú)名的或者命名的)及消息隊(duì)列來(lái)達(dá)到相同的目的。這篇文章將目光從高處(套接字)然后到低處(信號(hào))來(lái)關(guān)注 IPC。代碼示例將用力地充實(shí)下面的解釋細(xì)節(jié)。

套接字

正如管道有兩種類(lèi)型(命名和無(wú)名)一樣,套接字也有兩種類(lèi)型。IPC 套接字(即 Unix 套接字)給予進(jìn)程在相同設(shè)備(主機(jī))上基于通道的通信能力;而網(wǎng)絡(luò)套接字給予進(jìn)程運(yùn)行在不同主機(jī)的能力,因此也帶來(lái)了網(wǎng)絡(luò)通信的能力。網(wǎng)絡(luò)套接字需要底層協(xié)議的支持,例如 TCP(傳輸控制協(xié)議)或 UDP(用戶數(shù)據(jù)報(bào)協(xié)議)。

與之相反,IPC 套接字依賴于本地系統(tǒng)內(nèi)核的支持來(lái)進(jìn)行通信;特別的,IPC 通信使用一個(gè)本地的文件作為套接字地址。盡管這兩種套接字的實(shí)現(xiàn)有所不同,但在本質(zhì)上,IPC 套接字和網(wǎng)絡(luò)套接字的 API 是一致的。接下來(lái)的例子將包含網(wǎng)絡(luò)套接字的內(nèi)容,但示例服務(wù)器和客戶端程序可以在相同的機(jī)器上運(yùn)行,因?yàn)榉?wù)器使用了 localhost(127.0.0.1)這個(gè)網(wǎng)絡(luò)地址,該地址表示的是本地機(jī)器上的本地機(jī)器地址。

套接字以流的形式(下面將會(huì)討論到)被配置為雙向的,并且其控制遵循 C/S(客戶端/服務(wù)器端)模式:客戶端通過(guò)嘗試連接一個(gè)服務(wù)器來(lái)初始化對(duì)話,而服務(wù)器端將嘗試接受該連接。假如萬(wàn)事順利,來(lái)自客戶端的請(qǐng)求和來(lái)自服務(wù)器端的響應(yīng)將通過(guò)管道進(jìn)行傳輸,直到其中任意一方關(guān)閉該通道,從而斷開(kāi)這個(gè)連接。

一個(gè)迭代服務(wù)器(只適用于開(kāi)發(fā))將一直和連接它的客戶端打交道:從最開(kāi)始服務(wù)第一個(gè)客戶端,然后到這個(gè)連接關(guān)閉,然后服務(wù)第二個(gè)客戶端,循環(huán)往復(fù)。這種方式的一個(gè)缺點(diǎn)是處理一個(gè)特定的客戶端可能會(huì)掛起,使得其他的客戶端一直在后面等待。生產(chǎn)級(jí)別的服務(wù)器將是并發(fā)的,通常使用了多進(jìn)程或者多線程的混合。例如,我臺(tái)式機(jī)上的 Nginx 網(wǎng)絡(luò)服務(wù)器有一個(gè) 4 個(gè)工人worker的進(jìn)程池,它們可以并發(fā)地處理客戶端的請(qǐng)求。在下面的代碼示例中,我們將使用迭代服務(wù)器,使得我們將要處理的問(wèn)題保持在一個(gè)很小的規(guī)模,只關(guān)注基本的 API,而不去關(guān)心并發(fā)的問(wèn)題。

最后,隨著各種 POSIX 改進(jìn)的出現(xiàn),套接字 API 隨著時(shí)間的推移而發(fā)生了顯著的變化。當(dāng)前針對(duì)服務(wù)器端和客戶端的示例代碼特意寫(xiě)的比較簡(jiǎn)單,但是它著重強(qiáng)調(diào)了基于流的套接字中連接的雙方。下面是關(guān)于流控制的一個(gè)總結(jié),其中服務(wù)器端在一個(gè)終端中開(kāi)啟,而客戶端在另一個(gè)不同的終端中開(kāi)啟:

  • 服務(wù)器端等待客戶端的連接,對(duì)于給定的一個(gè)成功連接,它就讀取來(lái)自客戶端的數(shù)據(jù)。
  • 為了強(qiáng)調(diào)是雙方的會(huì)話,服務(wù)器端會(huì)對(duì)接收自客戶端的數(shù)據(jù)做回應(yīng)。這些數(shù)據(jù)都是 ASCII 字符代碼,它們組成了一些書(shū)的標(biāo)題。
  • 客戶端將書(shū)的標(biāo)題寫(xiě)給服務(wù)器端的進(jìn)程,并從服務(wù)器端的回應(yīng)中讀取到相同的標(biāo)題。然后客戶端和服務(wù)器端都在屏幕上打印出標(biāo)題。下面是服務(wù)器端的輸出,客戶端的輸出也和它完全一樣:
  1. Listening on port 9876 for clients...
  2. War and Peace
  3. Pride and Prejudice
  4. The Sound and the Fury

示例 1. 使用套接字的客戶端程序

  1. #include <string.h>
  2. #include <stdio.h>
  3. #include <stdlib.h>
  4. #include <unistd.h>
  5. #include <sys/types.h>
  6. #include <sys/socket.h>
  7. #include <netinet/tcp.h>
  8. #include <arpa/inet.h>
  9. #include "sock.h"
  10.  
  11. void report(const char* msg, int terminate) {
  12. perror(msg);
  13. if (terminate) exit(-1); /* failure */
  14. }
  15.  
  16. int main() {
  17. int fd = socket(AF_INET, /* network versus AF_LOCAL */
  18. SOCK_STREAM, /* reliable, bidirectional: TCP */
  19. 0); /* system picks underlying protocol */
  20. if (fd < 0) report("socket", 1); /* terminate */
  21. /* bind the server's local address in memory */
  22. struct sockaddr_in saddr;
  23. memset(&saddr, 0, sizeof(saddr)); /* clear the bytes */
  24. saddr.sin_family = AF_INET; /* versus AF_LOCAL */
  25. saddr.sin_addr.s_addr = htonl(INADDR_ANY); /* host-to-network endian */
  26. saddr.sin_port = htons(PortNumber); /* for listening */
  27. if (bind(fd, (struct sockaddr *) &saddr, sizeof(saddr)) < 0)
  28. report("bind", 1); /* terminate */
  29. /* listen to the socket */
  30. if (listen(fd, MaxConnects) < 0) /* listen for clients, up to MaxConnects */
  31. report("listen", 1); /* terminate */
  32.  
  33. fprintf(stderr, "Listening on port %i for clients...\n", PortNumber);
  34. /* a server traditionally listens indefinitely */
  35. while (1) {
  36. struct sockaddr_in caddr; /* client address */
  37. int len = sizeof(caddr); /* address length could change */
  38. int client_fd = accept(fd, (struct sockaddr*) &caddr, &len); /* accept blocks */
  39. if (client_fd < 0) {
  40. report("accept", 0); /* don't terminated, though there's a problem */
  41. continue;
  42. }
  43.  
  44. /* read from client */
  45. int i;
  46. for (i = 0; i < ConversationLen; i++) {
  47. char buffer[BuffSize + 1];
  48. memset(buffer, '\0', sizeof(buffer));
  49. int count = read(client_fd, buffer, sizeof(buffer));
  50. if (count > 0) {
  51. puts(buffer);
  52. write(client_fd, buffer, sizeof(buffer)); /* echo as confirmation */
  53. }
  54. }
  55. close(client_fd); /* break connection */
  56. } /* while(1) */
  57. return 0;
  58. }

上面的服務(wù)器端程序執(zhí)行典型的 4 個(gè)步驟來(lái)準(zhǔn)備回應(yīng)客戶端的請(qǐng)求,然后接受其他的獨(dú)立請(qǐng)求。這里每一個(gè)步驟都以服務(wù)器端程序調(diào)用的系統(tǒng)函數(shù)來(lái)命名。

  1. socket(…):為套接字連接獲取一個(gè)文件描述符
  2. bind(…):將套接字和服務(wù)器主機(jī)上的一個(gè)地址進(jìn)行綁定
  3. listen(…):監(jiān)聽(tīng)客戶端請(qǐng)求
  4. accept(…):接受一個(gè)特定的客戶端請(qǐng)求

上面的 socket 調(diào)用的完整形式為:

  1. int sockfd = socket(AF_INET,      /* versus AF_LOCAL */
  2.                     SOCK_STREAM,  /* reliable, bidirectional */
  3.                     0);           /* system picks protocol (TCP) */

第一個(gè)參數(shù)特別指定了使用的是一個(gè)網(wǎng)絡(luò)套接字,而不是 IPC 套接字。對(duì)于第二個(gè)參數(shù)有多種選項(xiàng),但 SOCK_STREAMSOCK_DGRAM(數(shù)據(jù)報(bào))是最為常用的。基于流的套接字支持可信通道,在這種通道中如果發(fā)生了信息的丟失或者更改,都將會(huì)被報(bào)告。這種通道是雙向的,并且從一端到另外一端的有效載荷在大小上可以是任意的。相反的,基于數(shù)據(jù)報(bào)的套接字大多是不可信的,沒(méi)有方向性,并且需要固定大小的載荷。socket 的第三個(gè)參數(shù)特別指定了協(xié)議。對(duì)于這里展示的基于流的套接字,只有一種協(xié)議選擇:TCP,在這里表示的 0。因?yàn)閷?duì) socket 的一次成功調(diào)用將返回相似的文件描述符,套接字可以被讀寫(xiě),對(duì)應(yīng)的語(yǔ)法和讀寫(xiě)一個(gè)本地文件是類(lèi)似的。

對(duì) bind 的調(diào)用是最為復(fù)雜的,因?yàn)樗从吵隽嗽谔捉幼?API 方面上的各種改進(jìn)。我們感興趣的點(diǎn)是這個(gè)調(diào)用將一個(gè)套接字和服務(wù)器端所在機(jī)器中的一個(gè)內(nèi)存地址進(jìn)行綁定。但對(duì) listen 的調(diào)用就非常直接了:

  1. if (listen(fd, MaxConnects) < 0)

第一個(gè)參數(shù)是套接字的文件描述符,第二個(gè)參數(shù)則指定了在服務(wù)器端處理一個(gè)拒絕連接錯(cuò)誤之前,有多少個(gè)客戶端連接被允許連接。(在頭文件 sock.hMaxConnects 的值被設(shè)置為 8。)

accept 調(diào)用默認(rèn)將是一個(gè)阻塞等待:服務(wù)器端將不做任何事情直到一個(gè)客戶端嘗試連接它,然后進(jìn)行處理。accept 函數(shù)返回的值如果是 -1 則暗示有錯(cuò)誤發(fā)生。假如這個(gè)調(diào)用是成功的,則它將返回另一個(gè)文件描述符,這個(gè)文件描述符被用來(lái)指代另一個(gè)可讀可寫(xiě)的套接字,它與 accept 調(diào)用中的第一個(gè)參數(shù)對(duì)應(yīng)的接收套接字有所不同。服務(wù)器端使用這個(gè)可讀可寫(xiě)的套接字來(lái)從客戶端讀取請(qǐng)求然后寫(xiě)回它的回應(yīng)。接收套接字只被用于接受客戶端的連接。

在設(shè)計(jì)上,服務(wù)器端可以一直運(yùn)行下去。當(dāng)然服務(wù)器端可以通過(guò)在命令行中使用 Ctrl+C 來(lái)終止它。

示例 2. 使用套接字的客戶端

  1. #include <string.h>
  2. #include <stdio.h>
  3. #include <stdlib.h>
  4. #include <unistd.h>
  5. #include <sys/types.h>
  6. #include <sys/socket.h>
  7. #include <arpa/inet.h>
  8. #include <netinet/in.h>
  9. #include <netinet/tcp.h>
  10. #include <netdb.h>
  11. #include "sock.h"
  12.  
  13. const char* books[] = {"War and Peace",
  14. "Pride and Prejudice",
  15. "The Sound and the Fury"};
  16.  
  17. void report(const char* msg, int terminate) {
  18. perror(msg);
  19. if (terminate) exit(-1); /* failure */
  20. }
  21.  
  22. int main() {
  23. /* fd for the socket */
  24. int sockfd = socket(AF_INET, /* versus AF_LOCAL */
  25. SOCK_STREAM, /* reliable, bidirectional */
  26. 0); /* system picks protocol (TCP) */
  27. if (sockfd < 0) report("socket", 1); /* terminate */
  28.  
  29. /* get the address of the host */
  30. struct hostent* hptr = gethostbyname(Host); /* localhost: 127.0.0.1 */
  31. if (!hptr) report("gethostbyname", 1); /* is hptr NULL? */
  32. if (hptr->h_addrtype != AF_INET) /* versus AF_LOCAL */
  33. report("bad address family", 1);
  34. /* connect to the server: configure server's address 1st */
  35. struct sockaddr_in saddr;
  36. memset(&saddr, 0, sizeof(saddr));
  37. saddr.sin_family = AF_INET;
  38. saddr.sin_addr.s_addr =
  39. ((struct in_addr*) hptr->h_addr_list[0])->s_addr;
  40. saddr.sin_port = htons(PortNumber); /* port number in big-endian */
  41. if (connect(sockfd, (struct sockaddr*) &saddr, sizeof(saddr)) < 0)
  42. report("connect", 1);
  43. /* Write some stuff and read the echoes. */
  44. puts("Connect to server, about to write some stuff...");
  45. int i;
  46. for (i = 0; i < ConversationLen; i++) {
  47. if (write(sockfd, books[i], strlen(books[i])) > 0) {
  48. /* get confirmation echoed from server and print */
  49. char buffer[BuffSize + 1];
  50. memset(buffer, '\0', sizeof(buffer));
  51. if (read(sockfd, buffer, sizeof(buffer)) > 0)
  52. puts(buffer);
  53. }
  54. }
  55. puts("Client done, about to exit...");
  56. close(sockfd); /* close the connection */
  57. return 0;
  58. }

客戶端程序的設(shè)置代碼和服務(wù)器端類(lèi)似。兩者主要的區(qū)別既不是在于監(jiān)聽(tīng)也不在于接收,而是連接:

  1. if (connect(sockfd, (struct sockaddr*) &saddr, sizeof(saddr)) < 0)

對(duì) connect 的調(diào)用可能因?yàn)槎喾N原因而導(dǎo)致失敗,例如客戶端擁有錯(cuò)誤的服務(wù)器端地址或者已經(jīng)有太多的客戶端連接上了服務(wù)器端。假如 connect 操作成功,客戶端將在一個(gè) for 循環(huán)中,寫(xiě)入它的請(qǐng)求然后讀取返回的響應(yīng)。在會(huì)話后,服務(wù)器端和客戶端都將調(diào)用 close 去關(guān)閉這個(gè)可讀可寫(xiě)套接字,盡管任何一邊的關(guān)閉操作就足以關(guān)閉它們之間的連接。此后客戶端可以退出了,但正如前面提到的那樣,服務(wù)器端可以一直保持開(kāi)放以處理其他事務(wù)。

從上面的套接字示例中,我們看到了請(qǐng)求信息被回顯給客戶端,這使得客戶端和服務(wù)器端之間擁有進(jìn)行豐富對(duì)話的可能性。也許這就是套接字的主要魅力。在現(xiàn)代系統(tǒng)中,客戶端應(yīng)用(例如一個(gè)數(shù)據(jù)庫(kù)客戶端)和服務(wù)器端通過(guò)套接字進(jìn)行通信非常常見(jiàn)。正如先前提及的那樣,本地 IPC 套接字和網(wǎng)絡(luò)套接字只在某些實(shí)現(xiàn)細(xì)節(jié)上面有所不同,一般來(lái)說(shuō),IPC 套接字有著更低的消耗和更好的性能。它們的通信 API 基本是一樣的。

信號(hào)

信號(hào)會(huì)中斷一個(gè)正在執(zhí)行的程序,在這種意義下,就是用信號(hào)與這個(gè)程序進(jìn)行通信。大多數(shù)的信號(hào)要么可以被忽略(阻塞)或者被處理(通過(guò)特別設(shè)計(jì)的代碼)。SIGSTOP (暫停)和 SIGKILL(立即停止)是最應(yīng)該提及的兩種信號(hào)。這種符號(hào)常量有整數(shù)類(lèi)型的值,例如 SIGKILL 對(duì)應(yīng)的值為 9

信號(hào)可以在與用戶交互的情況下發(fā)生。例如,一個(gè)用戶從命令行中敲了 Ctrl+C 來(lái)終止一個(gè)從命令行中啟動(dòng)的程序;Ctrl+C 將產(chǎn)生一個(gè) SIGTERM 信號(hào)。SIGTERM 意即終止,它可以被阻塞或者被處理,而不像 SIGKILL 信號(hào)那樣。一個(gè)進(jìn)程也可以通過(guò)信號(hào)和另一個(gè)進(jìn)程通信,這樣使得信號(hào)也可以作為一種 IPC 機(jī)制。

考慮一下一個(gè)多進(jìn)程應(yīng)用,例如 Nginx 網(wǎng)絡(luò)服務(wù)器是如何被另一個(gè)進(jìn)程優(yōu)雅地關(guān)閉的。kill 函數(shù):

  1. int kill(pid_t pid, int signum); /* declaration */

可以被一個(gè)進(jìn)程用來(lái)終止另一個(gè)進(jìn)程或者一組進(jìn)程。假如 kill 函數(shù)的第一個(gè)參數(shù)是大于 0 的,那么這個(gè)參數(shù)將會(huì)被認(rèn)為是目標(biāo)進(jìn)程的 pid(進(jìn)程 ID),假如這個(gè)參數(shù)是 0,則這個(gè)參數(shù)將會(huì)被視作信號(hào)發(fā)送者所屬的那組進(jìn)程。

kill 的第二個(gè)參數(shù)要么是一個(gè)標(biāo)準(zhǔn)的信號(hào)數(shù)字(例如 SIGTERMSIGKILL),要么是 0 ,這將會(huì)對(duì)信號(hào)做一次詢問(wèn),確認(rèn)第一個(gè)參數(shù)中的 pid 是否是有效的。這樣優(yōu)雅地關(guān)閉一個(gè)多進(jìn)程應(yīng)用就可以通過(guò)向組成該應(yīng)用的一組進(jìn)程發(fā)送一個(gè)終止信號(hào)來(lái)完成,具體來(lái)說(shuō)就是調(diào)用一個(gè) kill 函數(shù),使得這個(gè)調(diào)用的第二個(gè)參數(shù)是 SIGTERM 。(Nginx 主進(jìn)程可以通過(guò)調(diào)用 kill 函數(shù)來(lái)終止其他工人進(jìn)程,然后再停止自己。)就像許多庫(kù)函數(shù)一樣,kill 函數(shù)通過(guò)一個(gè)簡(jiǎn)單的可變語(yǔ)法擁有更多的能力和靈活性。

示例 3. 一個(gè)多進(jìn)程系統(tǒng)的優(yōu)雅停止

  1. #include <stdio.h>
  2. #include <signal.h>
  3. #include <stdlib.h>
  4. #include <unistd.h>
  5. #include <sys/wait.h>
  6.  
  7. void graceful(int signum) {
  8.   printf("\tChild confirming received signal: %i\n", signum);
  9.   puts("\tChild about to terminate gracefully...");
  10.   sleep(1);
  11.   puts("\tChild terminating now...");
  12.   _exit(0); /* fast-track notification of parent */
  13. }
  14.  
  15. void set_handler() {
  16.   struct sigaction current;
  17.   sigemptyset(&current.sa_mask);         /* clear the signal set */
  18.   current.sa_flags = 0;                  /* enables setting sa_handler, not sa_action */
  19.   current.sa_handler = graceful;         /* specify a handler */
  20.   sigaction(SIGTERM, &current, NULL);    /* register the handler */
  21. }
  22.  
  23. void child_code() {
  24.   set_handler();
  25.  
  26.   while (1) {   /` loop until interrupted `/
  27.     sleep(1);
  28.     puts("\tChild just woke up, but going back to sleep.");
  29.   }
  30. }
  31.  
  32. void parent_code(pid_t cpid) {
  33.   puts("Parent sleeping for a time...");
  34.   sleep(5);
  35.  
  36.   /* Try to terminate child. */
  37.   if (-1 == kill(cpid, SIGTERM)) {
  38.     perror("kill");
  39.     exit(-1);
  40.   }
  41.   wait(NULL); /` wait for child to terminate `/
  42.   puts("My child terminated, about to exit myself...");
  43. }
  44.  
  45. int main() {
  46.   pid_t pid = fork();
  47.   if (pid < 0) {
  48.     perror("fork");
  49.     return -1; /* error */
  50.   }
  51.   if (0 == pid)
  52.     child_code();
  53.   else
  54.     parent_code(pid);
  55.   return 0;  /* normal */
  56. }

上面的停止程序模擬了一個(gè)多進(jìn)程系統(tǒng)的優(yōu)雅退出,在這個(gè)例子中,這個(gè)系統(tǒng)由一個(gè)父進(jìn)程和一個(gè)子進(jìn)程組成。這次模擬的工作流程如下:

  • 父進(jìn)程嘗試去 fork 一個(gè)子進(jìn)程。假如這個(gè) fork 操作成功了,每個(gè)進(jìn)程就執(zhí)行它自己的代碼:子進(jìn)程就執(zhí)行函數(shù) child_code,而父進(jìn)程就執(zhí)行函數(shù) parent_code
  • 子進(jìn)程將會(huì)進(jìn)入一個(gè)潛在的無(wú)限循環(huán),在這個(gè)循環(huán)中子進(jìn)程將睡眠一秒,然后打印一個(gè)信息,接著再次進(jìn)入睡眠狀態(tài),以此循環(huán)往復(fù)。來(lái)自父進(jìn)程的一個(gè) SIGTERM 信號(hào)將引起子進(jìn)程去執(zhí)行一個(gè)信號(hào)處理回調(diào)函數(shù) graceful。這樣這個(gè)信號(hào)就使得子進(jìn)程可以跳出循環(huán),然后進(jìn)行子進(jìn)程和父進(jìn)程之間的優(yōu)雅終止。在終止之前,進(jìn)程將打印一個(gè)信息。
  • fork 一個(gè)子進(jìn)程后,父進(jìn)程將睡眠 5 秒,使得子進(jìn)程可以執(zhí)行一會(huì)兒;當(dāng)然在這個(gè)模擬中,子進(jìn)程大多數(shù)時(shí)間都在睡眠。然后父進(jìn)程調(diào)用 SIGTERM 作為第二個(gè)參數(shù)的 kill 函數(shù),等待子進(jìn)程的終止,然后自己再終止。

下面是一次運(yùn)行的輸出:

  1. % ./shutdown
  2. Parent sleeping for a time...
  3.         Child just woke up, but going back to sleep.
  4.         Child just woke up, but going back to sleep.
  5.         Child just woke up, but going back to sleep.
  6.         Child just woke up, but going back to sleep.
  7.         Child confirming received signal: 15  ## SIGTERM is 15
  8.         Child about to terminate gracefully...
  9.         Child terminating now...
  10. My child terminated, about to exit myself...

對(duì)于信號(hào)的處理,上面的示例使用了 sigaction 庫(kù)函數(shù)(POSIX 推薦的用法)而不是傳統(tǒng)的 signal 函數(shù),signal 函數(shù)有移植性問(wèn)題。下面是我們主要關(guān)心的代碼片段:

  • 假如對(duì) fork 的調(diào)用成功了,父進(jìn)程將執(zhí)行 parent_code 函數(shù),而子進(jìn)程將執(zhí)行 child_code 函數(shù)。在給子進(jìn)程發(fā)送信號(hào)之前,父進(jìn)程將會(huì)等待 5 秒:

    1. puts("Parent sleeping for a time...");
    2. sleep(5);
    3. if (-1 == kill(cpid, SIGTERM)) {
    4. ...sleepkillcpidSIGTERM...

    假如 kill 調(diào)用成功了,父進(jìn)程將在子進(jìn)程終止時(shí)做等待,使得子進(jìn)程不會(huì)變成一個(gè)僵尸進(jìn)程。在等待完成后,父進(jìn)程再退出。

  • child_code 函數(shù)首先調(diào)用 set_handler 然后進(jìn)入它的可能永久睡眠的循環(huán)。下面是我們將要查看的 set_handler 函數(shù):

    1. void set_handler() {
    2.   struct sigaction current;            /* current setup */
    3.   sigemptyset(&current.sa_mask);       /* clear the signal set */
    4.   current.sa_flags = 0;                /* for setting sa_handler, not sa_action */
    5.   current.sa_handler = graceful;       /* specify a handler */
    6.   sigaction(SIGTERM, &current, NULL);  /* register the handler */
    7. }

    上面代碼的前三行在做相關(guān)的準(zhǔn)備。第四個(gè)語(yǔ)句將為 graceful 設(shè)定為句柄,它將在調(diào)用 _exit 來(lái)停止之前打印一些信息。第 5 行和最后一行的語(yǔ)句將通過(guò)調(diào)用 sigaction 來(lái)向系統(tǒng)注冊(cè)上面的句柄。sigaction 的第一個(gè)參數(shù)是 SIGTERM ,用作終止;第二個(gè)參數(shù)是當(dāng)前的 sigaction 設(shè)定,而最后的參數(shù)(在這個(gè)例子中是 NULL )可被用來(lái)保存前面的 sigaction 設(shè)定,以備后面的可能使用。

使用信號(hào)來(lái)作為 IPC 的確是一個(gè)很輕量的方法,但確實(shí)值得嘗試。通過(guò)信號(hào)來(lái)做 IPC 顯然可以被歸入 IPC 工具箱中。

這個(gè)系列的總結(jié)

在這個(gè)系列中,我們通過(guò)三篇有關(guān) IPC 的文章,用示例代碼介紹了如下機(jī)制:

  • 共享文件
  • 共享內(nèi)存(通過(guò)信號(hào)量)
  • 管道(命名和無(wú)名)
  • 消息隊(duì)列
  • 套接字
  • 信號(hào)

甚至在今天,在以線程為中心的語(yǔ)言,例如 Java、C# 和 Go 等變得越來(lái)越流行的情況下,IPC 仍然很受歡迎,因?yàn)橄啾扔谑褂枚嗑€程,通過(guò)多進(jìn)程來(lái)實(shí)現(xiàn)并發(fā)有著一個(gè)明顯的優(yōu)勢(shì):默認(rèn)情況下,每個(gè)進(jìn)程都有它自己的地址空間,除非使用了基于共享內(nèi)存的 IPC 機(jī)制(為了達(dá)到安全的并發(fā),競(jìng)爭(zhēng)條件在多線程和多進(jìn)程的時(shí)候必須被加上鎖),在多進(jìn)程中可以排除掉基于內(nèi)存的競(jìng)爭(zhēng)條件。對(duì)于任何一個(gè)寫(xiě)過(guò)即使是基本的通過(guò)共享變量來(lái)通信的多線程程序的人來(lái)說(shuō),他都會(huì)知道想要寫(xiě)一個(gè)清晰、高效、線程安全的代碼是多么具有挑戰(zhàn)性。使用單線程的多進(jìn)程的確是很有吸引力的,這是一個(gè)切實(shí)可行的方式,使用它可以利用好今天多處理器的機(jī)器,而不需要面臨基于內(nèi)存的競(jìng)爭(zhēng)條件的風(fēng)險(xiǎn)。

當(dāng)然,沒(méi)有一個(gè)簡(jiǎn)單的答案能夠回答上述 IPC 機(jī)制中的哪一個(gè)更好。在編程中每一種 IPC 機(jī)制都會(huì)涉及到一個(gè)取舍問(wèn)題:是追求簡(jiǎn)潔,還是追求功能強(qiáng)大。以信號(hào)來(lái)舉例,它是一個(gè)相對(duì)簡(jiǎn)單的 IPC 機(jī)制,但并不支持多個(gè)進(jìn)程之間的豐富對(duì)話。假如確實(shí)需要這樣的對(duì)話,另外的選擇可能會(huì)更合適一些。帶有鎖的共享文件則相對(duì)直接,但是當(dāng)要處理大量共享的數(shù)據(jù)流時(shí),共享文件并不能很高效地工作。管道,甚至是套接字,有著更復(fù)雜的 API,可能是更好的選擇。讓具體的問(wèn)題去指導(dǎo)我們的選擇吧。

盡管所有的示例代碼(可以在我的網(wǎng)站上獲取到)都是使用 C 寫(xiě)的,其他的編程語(yǔ)言也經(jīng)常提供這些 IPC 機(jī)制的輕量包裝。這些代碼示例都足夠短小簡(jiǎn)單,希望這樣能夠鼓勵(lì)你去進(jìn)行實(shí)驗(yàn)。 

責(zé)任編輯:龐桂玉 來(lái)源: Linux中國(guó)
相關(guān)推薦

2010-01-05 10:00:48

Linux進(jìn)程間通信

2019-05-08 11:10:05

Linux進(jìn)程語(yǔ)言

2019-05-13 10:00:41

Linux進(jìn)程間通信命令

2024-01-03 10:17:51

Linux通信

2016-11-28 14:11:24

ANDROID BIN通信架構(gòu)

2017-08-06 00:05:18

進(jìn)程通信開(kāi)發(fā)

2018-05-30 13:58:02

Linux進(jìn)程通信

2017-06-19 13:36:12

Linux進(jìn)程消息隊(duì)列

2018-01-12 14:35:00

Linux進(jìn)程共享內(nèi)存

2010-07-06 15:33:10

UDP套接字

2011-06-22 17:09:50

QT 進(jìn)程 通信

2020-11-04 07:17:42

Nodejs通信進(jìn)程

2025-04-27 03:22:00

2013-03-28 13:14:45

AIDL進(jìn)程間通信Android使用AI

2022-07-04 08:29:13

electron通信

2019-11-27 10:36:11

進(jìn)程通信IPC

2019-11-08 14:47:49

TCPIP網(wǎng)絡(luò)

2025-05-13 07:10:31

2011-06-24 14:01:34

Qt QCOP 協(xié)議

2025-05-07 08:03:10

點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)

99re91这里只有精品| 九色蝌蚪在线| 亚洲黄色三级| 亚洲欧美日韩中文在线制服| 在线观看国产一级片| 日本片在线观看| 久久综合九色欧美综合狠狠| 成人写真视频福利网| 久久久精品福利| 亚洲综合五月| 国产一区二区三区欧美| 三大队在线观看| 亚洲第一会所001| 亚洲高清久久久| 日本一区高清不卡| 成人精品在线播放| 奇米一区二区三区| 午夜伦理精品一区| www.毛片com| av亚洲免费| 亚洲精品电影网在线观看| 国产毛片久久久久久| 另类图片综合电影| 亚洲成va人在线观看| 手机看片日韩国产| 成年人视频在线观看免费| 国产a级毛片一区| 国产一区二区在线免费| 国产精品suv一区| 亚洲片区在线| 欧美激情一区二区三级高清视频| 国产成人免费在线观看视频| 国产成人3p视频免费观看| 亚洲激情自拍图| 欧美成人精品一区二区综合免费| 亚洲欧美专区| 欧美日韩在线精品一区二区三区激情| 欧美日韩亚洲一| 国产99在线| 五月天欧美精品| 给我免费播放片在线观看| 婷婷色在线播放| 亚洲精品国产品国语在线app| 亚洲一区二区三区色| 成年在线电影| 国产欧美精品一区| 日韩欧美三级电影| 大片免费播放在线视频| 欧美国产日韩在线观看| 日本欧美精品久久久| 国产香蕉在线| 国产农村妇女精品| 亚洲一区二区三区涩| 91精品国产综合久久久久久豆腐| 久久精品一区蜜桃臀影院| 免费成人深夜夜行视频| 久久久久久久久亚洲精品| 久久女同精品一区二区| 欧美性色黄大片人与善| 国产在线免费观看| 欧美国产日韩精品免费观看| 日本一区二区三区免费看| 国产高清视频在线观看| 国产精品美女久久福利网站| 少妇免费毛片久久久久久久久 | 国产精彩视频在线| 亚洲精品看片| 国产不卡在线观看| 在线观看日批视频| 国产精品资源在线观看| 超碰97人人人人人蜜桃| 天天躁日日躁狠狠躁伊人| 26uuu色噜噜精品一区| 欧美一级爱爱| 日本蜜桃在线观看| 一区二区国产视频| 草草久久久无码国产专区| 欧美成人ⅴideosxxxxx| 欧美久久一二区| youjizz.com日本| 亚洲人成伊人成综合图片| 中文字幕亚洲情99在线| 澳门黄色一级片| 亚洲九九精品| 国产欧美日韩免费| 国模人体一区二区| 久久久久国产精品免费免费搜索| 亚洲综合网中心| caoporn-草棚在线视频最| 色偷偷88欧美精品久久久| 91视频这里只有精品| 国产精品任我爽爆在线播放| 国产亚洲精品美女久久久| 免费在线观看h片| 亚洲欧美大片| 91久久久久久久久久久| 少妇性bbb搡bbb爽爽爽欧美| 亚洲三级在线免费观看| 国产91xxx| 看亚洲a级一级毛片| 亚洲欧洲偷拍精品| 久久久久久久国产精品毛片| 三级欧美在线一区| 国产高清精品一区二区| 91在线导航| 午夜电影网亚洲视频| 国产一级片自拍| 同性恋视频一区| 久热精品视频在线| 中文区中文字幕免费看| 97精品国产露脸对白| 成人短视频在线看| 日韩国产网站| 亚洲精品wwww| 国产一级在线播放| 国产一区二区在线视频| 小说区图片区图片区另类灬| 91九色国产在线播放| 91精品福利在线一区二区三区| 免费污网站在线观看| 亚洲性色视频| 91精品免费| 日韩免费网站| 欧美性大战久久久久久久蜜臀| 精品无码人妻少妇久久久久久| 91精品一区二区三区综合| 国产极品jizzhd欧美| 性感美女福利视频| 亚洲国产精品麻豆| 亚洲精品鲁一鲁一区二区三区| 91蜜臀精品国产自偷在线| 国产精品99久久久久久久久久久久| 免费观看黄一级视频| 亚洲激情图片一区| 女王人厕视频2ⅴk| 欧美一区91| 91在线无精精品一区二区| 日韩免费啪啪| 欧美人妇做爰xxxⅹ性高电影| 黄色片网站免费| 日韩高清不卡在线| 日韩一区免费观看| 国产韩日精品| 在线观看久久av| 中文字幕在线日亚洲9| 欧美激情中文字幕| 人人干人人干人人| 久久大综合网| 91天堂在线观看| 成人在线观看免费网站| 91精品久久久久久久91蜜桃| 加勒比婷婷色综合久久| 国产一区在线不卡| 国产又粗又大又爽的视频| 欧一区二区三区| 欧美精品xxx| 色欲av永久无码精品无码蜜桃| 黄色成人在线免费| 久久只有这里有精品| 日本在线不卡一区| 正在播放国产精品| 日韩三级不卡| 91精品国产一区| 成人免费在线电影| 欧美美女一区二区| 国产小视频在线观看免费| www.亚洲色图| 成人在线观看黄| 久久理论电影| 成人免费看片网址| 国产伦子伦对白在线播放观看| 亚洲美女动态图120秒| 自拍偷拍福利视频| 亚洲精品视频一区二区| 日本一卡二卡在线| 美国十次了思思久久精品导航| 在线免费观看成人| 加勒比色老久久爱综合网| 国产suv精品一区二区三区88区| 91最新在线| 精品国精品国产尤物美女| 亚洲欧美精品一区二区三区| 国产精品美女久久久久aⅴ国产馆| 久久久久久久久久久影视| 亚洲欧美日韩国产一区| 在线视频一区观看| 黄色美女久久久| 国产精品免费久久久| 天天色天天射天天综合网| 亚洲日本欧美中文幕| 国产片在线播放| 精品日本高清在线播放| 日本 欧美 国产| 91污片在线观看| 国产成人在线综合| 销魂美女一区二区三区视频在线| 最新av在线免费观看| 日韩高清影视在线观看| 成人有码在线播放| 精品欧美一区二区三区在线观看 | 999一区二区三区| 国产一区二区三区四区五区| 3d动漫啪啪精品一区二区免费 | av片在线免费看| 99久久综合狠狠综合久久| 粉色视频免费看| 午夜亚洲性色视频| 美女av免费观看| 色小子综合网| 欧美一区亚洲二区| 粉嫩一区二区三区四区公司1| 国产精品自产拍在线观| 久久久一本精品| 高清视频欧美一级| 亚洲精品白浆| www.欧美精品一二三区| 青青草观看免费视频在线| 精品国产乱码久久久久久久| 91色在线播放| 欧美视频一区二区三区四区| 国产精品国产三级国产专区52| 一区二区三区欧美日| 99久久久无码国产精品不卡| 国产亚洲一区二区在线观看| 国产白嫩美女无套久久| 福利一区二区在线| 992kp免费看片| 久久99国产乱子伦精品免费| 欧美黄色性生活| 丝袜美腿亚洲综合| 粗暴91大变态调教| 亚洲欧美卡通另类91av| 精品无码一区二区三区在线| 红桃视频国产精品| 成人国产在线看| 欧美国产高潮xxxx1819| 永久免费网站视频在线观看| 亚洲精品一区二区妖精| 色中文字幕在线观看| 欧美 日韩 国产 激情| 欧美一性一交| 精品视频免费观看| 中文在线综合| 亚洲免费观看高清完整版在线观看 | 亚洲成人777| 中文字幕一区二区三区手机版| 亚洲精品国产精华液| 久久r这里只有精品| 亚洲欧美日韩久久| 国产性xxxx| 亚洲黄色录像片| 国产在线视频99| 五月激情丁香一区二区三区| 好吊妞视频一区二区三区| 天天操天天色综合| 日本黄色一级视频| 欧美色图天堂网| 亚洲视频一区在线播放| 91麻豆精品国产综合久久久久久| 国产精品无码专区av免费播放| 91麻豆精品国产91久久久久久| 99视频在线观看免费| 精品国产一二三区| 日本私人网站在线观看| 一本大道亚洲视频| 免费不卡视频| 久久久久国产精品免费| 中国老头性行为xxxx| 韩国毛片一区二区三区| 性生交大片免费看l| 99久久久国产精品| 日本精品在线观看视频| 国产精品第四页| 午夜国产福利一区二区| 午夜视黄欧洲亚洲| 精品人妻一区二区三区潮喷在线| 欧美日本国产视频| 韩国av在线免费观看| 国产拍揄自揄精品视频麻豆| 日本午夜精品一区二区| 青青一区二区三区| 肉大捧一出免费观看网站在线播放 | 国产尤物在线视频| 欧美人与禽zozo性伦| 丰满熟妇乱又伦| 这里只有精品视频在线| 中文字幕在线观看播放| 欧美中文字幕在线| 亚洲精品66| 久久综合九九| 希岛爱理av一区二区三区| www.com毛片| 国产精品69毛片高清亚洲| 精品无码国产一区二区三区51安| 国产色产综合产在线视频| 丰满少妇高潮久久三区| 一本色道久久综合亚洲aⅴ蜜桃 | 亚洲国产精品女人久久久| 国产视频精品久久| 欧美国产精品日韩| 精品国产黄a∨片高清在线| 国产自产精品| 一区二区三区毛片免费| 欧美日韩第二页| 国产成人av网站| 四虎影视1304t| 天天综合日日夜夜精品| 国产三级第一页| 亚洲天堂成人在线视频| 成人爽a毛片免费啪啪动漫| 国产在线观看精品一区二区三区| 欧美亚洲tv| 免费在线黄网站| 久久66热偷产精品| 一区二区伦理片| 午夜精品123| 亚洲国产精品久久久久久6q| 久久精品青青大伊人av| 国产精品久久久久av电视剧| 国产欧美一区二区视频| 欧美日一区二区在线观看| 在线观看国产一级片| 国产午夜亚洲精品不卡| 四虎成人永久免费视频| 欧美精品一区二区三区视频| 97影院秋霞午夜在线观看| 成人激情在线播放| 日韩在线精品| www.99av.com| 国产午夜精品一区二区| 国产又大又粗又爽| 日韩高清a**址| av在线私库| 黄色99视频| 日韩伦理一区| 欧美成人午夜激情| 芒果视频成人app| 久久亚洲精品欧美| 一本久久综合| 在线观看www91| 91麻豆国产在线| www国产精品视频| 玖玖精品在线| 色噜噜狠狠一区二区三区| 久久五月激情| 丰满少妇高潮一区二区| 91久久线看在观草草青青| 国产尤物视频在线| 国产成人高清激情视频在线观看| 国产精品qvod| 欧美a在线视频| 久久久久久久精| 日本丰满少妇做爰爽爽| 中文欧美在线视频| 色综合视频一区二区三区日韩| 一区二区三区四区欧美日韩| 国产在线国偷精品产拍免费yy| 一区二区国产精品精华液| 日韩一区二区三区免费看| 成人超碰在线| 欧美激情第六页| 日本vs亚洲vs韩国一区三区| 国产精品视频看看| 精品乱人伦小说| 超碰超碰人人人人精品| 性欧美精品一区二区三区在线播放| 免费成人在线网站| 日韩在线观看视频一区二区| 亚洲成人黄色在线观看| 韩国主播福利视频一区二区三区| 午夜精品一区二区在线观看的| 美女视频第一区二区三区免费观看网站| 欧美性x x x| 精品国产1区2区3区| 日韩电影免费观| 中文字幕久精品免| 本田岬高潮一区二区三区| 中文字幕av影院| 欧美xxxx18国产| 久久不卡国产精品一区二区| 超碰人人草人人| 天天影视网天天综合色在线播放| 99re在线视频| 国产91免费视频| 日韩精品亚洲专区| 九九热只有精品| 亚洲欧美色图片| 亚洲国产精品免费视频| 18禁男女爽爽爽午夜网站免费| 国产精品美女一区二区在线观看| 亚洲大尺度视频| 国产精品久久久久影院日本 | 日韩中文字幕在线播放| av成人app永久免费| 日本人视频jizz页码69| 亚洲国产成人av| 欧美性videos| 久久久久久久免费| 国产一区二区免费在线| 亚洲s码欧洲m码国产av|