精品欧美一区二区三区在线观看 _久久久久国色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)

韩国19禁主播vip福利视频| 欧美日韩亚州综合| 欧美1o一11sex性hdhd| 日本熟女毛茸茸| 日韩免费高清| 欧美大片一区二区| 精品中文字幕av| 国产精品无码2021在线观看| 老司机免费视频一区二区三区| 久久的精品视频| 日本五十肥熟交尾| 91成人在线| 一区二区三区在线免费播放| 美女亚洲精品| www.成人精品| 日韩高清在线不卡| 九九热精品在线| 老司机福利av| 日韩高清二区| 欧美日韩在线播| 人妻少妇精品无码专区二区| 夜级特黄日本大片_在线| 成人午夜av电影| 国产精品嫩草影院一区二区| 一级aaa毛片| 热久久天天拍国产| 日韩av一区在线| 色姑娘综合天天| 日本综合久久| 亚洲成人你懂的| 一区二区视频在线免费| 性感美女视频一二三| 国模无码大尺度一区二区三区| 欧美一区二区三区……| 久草免费在线观看视频| 欧美成人精品一区二区三区在线看| 亚洲国产精品免费| 欧美日韩一区二区区别是什么| 欧美专区福利免费| 天天av天天翘天天综合网色鬼国产 | 成人一级片免费看| 天堂日韩电影| 亚洲精品国产精品久久清纯直播| 四虎国产精品永久免费观看视频| 成人在线观看免费视频| 日本高清无吗v一区| 国产自产在线视频| 青青在线视频| 一区二区三区免费看视频| 在线观看一区二区三区三州| аⅴ资源新版在线天堂| 国产清纯美女被跳蛋高潮一区二区久久w | 五月天av在线播放| 123成人网| 欧美日韩一区在线| 欧美在线aaa| 欧洲亚洲精品| 欧美一区欧美二区| 国产又粗又猛大又黄又爽| 国产亚洲高清在线观看| 欧美精品tushy高清| 自拍偷拍一区二区三区四区| а√天堂资源国产精品| 欧美蜜桃一区二区三区| 久久6免费视频| 免费精品一区| 亚洲激情自拍图| 亚洲国产精品无码久久久久高潮| 国产精品视屏| 亚洲美女在线视频| 永久免费毛片在线观看| 偷拍欧美精品| 欧美激情亚洲自拍| 51国产偷自视频区视频| 手机精品视频在线观看| 国产精品国模在线| 国产喷水吹潮视频www| 国产成人亚洲精品青草天美| 成人免费看片网址| 欧美xxx.com| 国产精品视频一二三区 | 欧美日韩1区| 91成人免费观看网站| 国产成人精品亚洲| 国产一区二区视频在线| 国产精品一区视频网站| 精品视频一二三| 亚洲天堂久久久久久久| 亚洲精品蜜桃久久久久久| 成人福利视频| 欧美一区二区三区免费观看视频| 久久久男人的天堂| 亚洲午夜久久| 久久久av电影| 久久精品女人毛片国产| 日日夜夜免费精品视频| 亚洲一区久久久| 婷婷国产在线| 中文字幕一区二区三区在线播放 | 成人av网站在线观看免费| 欧美日韩中文国产一区发布| 日本精品一区二区三区在线播放| 亚洲大型综合色站| 手机版av在线| 亚洲aaa级| 久久99精品国产99久久6尤物| 欧美性猛交bbbbb精品| 国产一区二区不卡在线| 日本免费高清一区二区| 九色91在线| 欧美美女一区二区| 中文字幕一区二区三区人妻不卡| 91成人超碰| 国产精品久久国产精品99gif| 99热这里只有精| 国产日韩欧美一区二区三区乱码| 激情五月婷婷六月| 24小时成人在线视频| 亚洲四色影视在线观看| 国产精品第一页在线观看| 日韩 欧美一区二区三区| 国产原创精品| 18videosex性欧美麻豆| 欧美日韩激情一区二区| 国产人妻一区二区| 国产精品日韩| 狠狠久久综合婷婷不卡| 国产在线看片| 欧美日韩一区二区在线观看| 四虎永久免费影院| 一区二区三区福利| 国产精品一区二区免费| 18videosex性欧美麻豆| 欧美一区午夜视频在线观看| 少妇无套高潮一二三区| 麻豆成人精品| 免费精品视频一区二区三区| 丰满大乳少妇在线观看网站| 欧美一区二区久久久| 影音先锋男人资源在线观看| 美女视频一区二区三区| 日韩精品欧美一区二区三区| 一区二区电影免费观看| 日韩风俗一区 二区| 亚洲国产精品成人无久久精品| 国产精品夜夜爽| 黄色网络在线观看| 亚洲精品观看| 久久久久久久香蕉网| 狠狠人妻久久久久久综合麻豆| 一区二区在线电影| 性猛交╳xxx乱大交| 黄色工厂这里只有精品| 国产伦精品一区二区三区照片| 美足av综合网| 日韩av中文在线| 亚洲精品男人天堂| 久久久久久久精| 99热手机在线| 日韩在线综合| 91青青草免费观看| 51精品在线| 日韩精品在线观看视频| 精品人妻无码一区二区性色| 久久久不卡影院| 国产精品久久久毛片| 999国产精品| 99在线影院| 爱啪视频在线观看视频免费| 亚洲人精选亚洲人成在线| 亚洲天堂久久久久| 亚洲激情男女视频| 麻豆精品国产传媒av| 视频一区欧美日韩| 日本特级黄色大片| 涩爱av色老久久精品偷偷鲁| 久久久欧美一区二区| 男人天堂亚洲二区| 欧美顶级少妇做爰| 日韩欧美激情视频| 中文字幕精品综合| 99热这里只有精品2| 日韩一级精品| 亚洲一卡二卡三卡| av综合网址| 国产成人精品日本亚洲专区61| 欧美jizzhd欧美| 精品国产自在久精品国产| www.国产com| 亚洲视频资源在线| 插吧插吧综合网| 国产精品一区在线观看乱码| 91视频 -- 69xx| 99久久婷婷| 精品一区久久久久久| 亚洲精品自拍| 欧美一级片一区| av大大超碰在线| 亚洲欧美中文日韩在线v日本| 国产精品亚洲lv粉色| 欧美日韩性视频在线| 美国一级片在线观看| aaa国产一区| 日韩精品aaa| 老司机免费视频久久| 人人妻人人澡人人爽欧美一区| 综合色就爱涩涩涩综合婷婷| 亚洲自拍偷拍区| 最新欧美电影| 国产69精品久久久久久| 超碰最新在线| 在线成人激情视频| 午夜小视频免费| 日韩精品一区二区三区swag| 一二区在线观看| 色成人在线视频| 国产精品111| 亚洲最新视频在线播放| 神马久久久久久久久久久| xfplay精品久久| 性高潮免费视频| 国产精品一卡二卡在线观看| 一区二区三区网址| 久久精品一本| 日本日本19xxxⅹhd乱影响| 午夜欧美精品| 蜜臀在线免费观看| 91视频久久| 午夜精品亚洲一区二区三区嫩草 | 国产91在线亚洲| 欧美高清视频手机在在线| 日韩免费三级| 精品久久久久久久久久久下田| 国产一区二区免费在线观看| 亚洲精选av| αv一区二区三区| 久久av网站| 91在线观看免费高清| 日韩专区视频网站| 成人高h视频在线| 青青草国产一区二区三区| 国产精品一区二区三区久久久| 欧美三级网址| 国产精品久久久久国产a级| japanese23hdxxxx日韩| 国产99久久精品一区二区永久免费 | 国产精品播放| 欧州一区二区三区| 99精品99久久久久久宅男| 少妇精品在线| 国产无套精品一区二区| 欧美交a欧美精品喷水| 久久国产精品99久久久久久丝袜| 国产欧美一区二区三区米奇| 国产精品久久亚洲7777| 亚洲一区网址| 国产一区二区在线网站| 乱亲女h秽乱长久久久| 久精品国产欧美| 国产欧美日韩| 亚洲三区在线| 在线精品国产| 91午夜在线观看| 午夜亚洲一区| wwwwwxxxx日本| 国产91高潮流白浆在线麻豆| 日韩av无码一区二区三区不卡| 91天堂素人约啪| 成人免费看aa片| 国产精品网站一区| 乱h高h女3p含苞待放| 亚洲成人综合网站| 天堂网视频在线| 337p亚洲精品色噜噜狠狠| 草草视频在线播放| 亚洲九九九在线观看| 中文字幕在线免费| 欧美老少配视频| 伊人久久综合一区二区| 国产精品视频一区国模私拍| 99久久久成人国产精品| 国产精品一区二区欧美| 激情综合网站| 免费看日本黄色| 老司机午夜精品视频在线观看| 色啦啦av综合| 91在线精品一区二区三区| ass极品国模人体欣赏| 一区二区视频免费在线观看| 9i精品福利一区二区三区| 91精品国产91热久久久做人人| 五月婷婷开心中文字幕| 色妞久久福利网| 蜜桃视频动漫在线播放| 成人网欧美在线视频| 任你躁在线精品免费| 一级一片免费播放| 国产一区二区你懂的| 想看黄色一级片| 久久久国际精品| 国产精彩视频在线| 欧美精品一二三四| 国产黄在线播放| 久久久久久国产精品久久| 国产亚洲精彩久久| 精选一区二区三区四区五区| 7777久久香蕉成人影院| 不卡影院一区二区| 国产a精品视频| 狂野欧美性猛交| 色综合久久中文字幕综合网| 精品人妻一区二区三区麻豆91| 一个人www欧美| 中文字幕21页在线看| 99九九视频| 久久久人成影片免费观看| 成人黄色一区二区| 99re热这里只有精品免费视频| 永久免费看片直接| 欧美伊人久久大香线蕉综合69 | 精品无码一区二区三区在线| 久久99精品久久久久久动态图| a视频免费观看| 午夜激情一区二区三区| 精品国产九九九| 久久综合免费视频| 天天综合在线观看| 色999日韩自偷自拍美女| 久久aⅴ国产紧身牛仔裤| 稀缺小u女呦精品呦| 一区二区三区在线高清| 国产乱叫456在线| 日韩网站免费观看| 久久精品xxxxx| 香蕉久久夜色| 日本欧美一区二区三区| 无码国产69精品久久久久同性| 欧美色videos| 欧洲成人av| 日本成人激情视频| 九九热线有精品视频99| 亚洲精品无码久久久久久| 91在线丨porny丨国产| 特级西西444www大精品视频免费看| 亚洲精品久久久一区二区三区 | 亚洲麻豆av| 在线黄色免费网站| 精品欧美激情精品一区| 天堂中文字幕在线| 国产91在线高潮白浆在线观看| 蜜桃一区二区| 久久精品免费网站| 国产精品麻豆网站| 国产精品一区二区免费视频| www.日韩.com| 视频欧美一区| 日韩中文字幕在线免费| 99re这里只有精品视频首页| 91看片在线播放| 国产一区二区三区18| 欧美v亚洲v综合v国产v仙踪林| 中文字幕不卡每日更新1区2区| 精品一区二区三区影院在线午夜 | 欧美午夜精品免费| 欧美a在线看| 国产精品久久久久久久久婷婷| 在线亚洲一区| 中文字幕人妻一区二区三区在线视频| 欧美日韩五月天| 日本无删减在线| 久久久久久国产精品mv| 日韩二区在线观看| 精品国产视频一区二区三区| 精品福利在线导航| 激情都市亚洲| 欧美 日韩 国产 在线观看| 国产成人8x视频一区二区| 国产又黄又猛又粗又爽| 深夜福利一区二区| 91久久精品无嫩草影院| 国产女大学生av| 国产精品久久精品日日| 粉嫩小泬无遮挡久久久久久| 欧美重口另类videos人妖| 日韩中文在线电影| 亚洲av成人片无码| 欧美日韩午夜影院| 成人观看网址| 午夜欧美性电影| 成人黄色av电影| 一级黄色免费片| 日本成人免费在线| 亚洲成人三区| 四虎永久免费在线观看| 日韩片之四级片| 日本另类视频| 欧美极品欧美精品欧美| 亚洲欧美影音先锋| 日本啊v在线| 高清国语自产拍免费一区二区三区|