自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

TCP/IP網(wǎng)絡(luò)編程之進程與進程間通信

網(wǎng)絡(luò) 通信技術(shù)
進程間通信意味著兩個不同進程間可以交換數(shù)據(jù),為了完成這一點,操作系統(tǒng)中應提供兩個進程可以同時訪問的內(nèi)存空間。但我們知道,進程具有完全獨立的內(nèi)存結(jié)構(gòu),就連通過fork函數(shù)創(chuàng)建的子進程也不會和父進程共享內(nèi)存,因此,進程間通信只能通過其他特殊方法完成。

 [[281737]]

進程間通信基本概念

進程間通信意味著兩個不同進程間可以交換數(shù)據(jù),為了完成這一點,操作系統(tǒng)中應提供兩個進程可以同時訪問的內(nèi)存空間。但我們知道,進程具有完全獨立的內(nèi)存結(jié)構(gòu),就連通過fork函數(shù)創(chuàng)建的子進程也不會和父進程共享內(nèi)存,因此,進程間通信只能通過其他特殊方法完成。

基于管道實現(xiàn)進程間通信

圖1-1表示基于管道(PIPE)的進程間通信結(jié)構(gòu)模型

 

TCP/IP網(wǎng)絡(luò)編程之進程與間通信

 

圖1-1 基于管道的進程間通信模型

從圖1-1可以看到,為了完成進程間通信,需要創(chuàng)建管道。管道并非屬于進程資源,而是和套接字一樣,屬于操作系統(tǒng)資源(也就不是fork函數(shù)的復制對象)。下面介紹創(chuàng)建管道函數(shù)

  1. #include 
  2. int pipe (int filedes[2]);//成功時返回0,失敗時返回-1 
  • filedes[0]:通過管道接收數(shù)據(jù)時使用的文件描述符,即管道出口
  • filedes[1]:通過管道傳輸數(shù)據(jù)時使用的文件描述符,即管道入口

以長度為2的int數(shù)組地址值作為參數(shù)調(diào)用上述函數(shù)時,數(shù)組中存有兩個文件描述符,它們將被用作管道的出口和入口。父進程調(diào)用該函數(shù)時將創(chuàng)建管道,同時獲取對應于出入口的文件描述符,此時父進程可以讀寫同一管道。但父進程的目的是與子進程進行數(shù)據(jù)交換,因此需要將入口和出口中的一個文件描述符傳遞給子進程,如何完成傳遞呢?答案還是調(diào)用fork函數(shù)。

  1. pipe1.c 
  2. #include <stdio.h> 
  3. #include <unistd.h> 
  4. #define BUF_SIZE 30 
  5. int main(int argc, char *argv[]) 
  6. int fds[2]; 
  7. char str[] = "Who are you?"
  8. char buf[BUF_SIZE]; 
  9. pid_t pid; 
  10. pipe(fds); 
  11. pid = fork(); 
  12. if (pid == 0) 
  13. write(fds[1], str, sizeof(str)); 
  14. else 
  15. read(fds[0], buf, BUF_SIZE); 
  16. puts(buf); 
  17. return 0; 
  • 第12行:調(diào)用pipe函數(shù)創(chuàng)建管道,fds數(shù)組中保存用于I/O的文件描述符
  • 第13行:接著調(diào)用fork函數(shù),子進程將同時擁有通過12行函數(shù)調(diào)用獲取的兩個文件描述符。注意!復制的并非管道,而是用于管道I/O的文件描述符。至此,父子進程同時擁有I/O文件描述符
  • 第16、20行:子進程通過第16行代碼向管道傳遞字符串,父進程通過第20行代碼從管道接收字符串

編譯pipe1.c并運行

  1. # gcc pipe1.c -o pipe1 
  2. # ./pipe1 
  3. Who are you? 

上述示例中的通信方法及路徑如圖1-2所示,重點在于,父子進程都可以訪問管道的I/O路徑,但子進程僅用輸入路徑,父進程僅用輸出路徑

 

TCP/IP網(wǎng)絡(luò)編程之進程與間通信

 

圖1-2 示例pipe1.c的通信路徑

以上就是管道的基本原理及通信方法,應用管道時還有一部分內(nèi)容需要注意,通過雙向通信示例進一步說明

通過管道進行進程間雙向通信

下面創(chuàng)建兩個進程通過一個管道進行雙向數(shù)據(jù)交換的示例,其通信方式如圖1-3所示

 

TCP/IP網(wǎng)絡(luò)編程之進程與間通信

 

圖1-3 雙向通信模型1

從圖1-3可以看出,通過一個管道可以進行雙向通信,但采用這種模型需格外小心,先給出示例,稍后再討論。

pipe2.c

  1. #include <stdio.h> 
  2. #include <unistd.h> 
  3. #define BUF_SIZE 30 
  4. int main(int argc, char *argv[]) 
  5. int fds[2]; 
  6. char str1[] = "Who are you?"
  7. char str2[] = "Thank you for your message"
  8. char buf[BUF_SIZE]; 
  9. pid_t pid; 
  10. pipe(fds); 
  11. pid = fork(); 
  12. if (pid == 0) 
  13. write(fds[1], str1, sizeof(str1)); 
  14. sleep(2); 
  15. read(fds[0], buf, BUF_SIZE); 
  16. printf("Child proc output: %s \n", buf); 
  17. else 
  18. read(fds[0], buf, BUF_SIZE); 
  19. printf("Parent proc output: %s \n", buf); 
  20. write(fds[1], str2, sizeof(str2)); 
  21. sleep(3); 
  22. return 0; 
  • 第17~20行:子進程運行區(qū)域,通過第17行行傳輸數(shù)據(jù),通過第19行接收數(shù)據(jù)。第18行的sleep函數(shù)至關(guān)重要,這一點稍后再討論
  • 第24~26行:父進程的運行區(qū)域,通過第24行接收數(shù)據(jù),這是為了接收第17行子進程傳輸?shù)臄?shù)據(jù)。另外通過第26行傳輸數(shù)據(jù),這些數(shù)據(jù)將被第19行的子進程接收
  • 第27行:父進程先終止時會彈出命令提示符,這時子進程仍然在工作,故不會產(chǎn)生問題。這條語句主要是為了防止子進程終止前彈出命令提示符(故可刪除)

編譯pipe2.c并運行

  1. # gcc pipe2.c -o pipe2 
  2. # ./pipe2 
  3. Parent proc output: Who are you? 
  4. Child proc output: Thank you for your message 

運行結(jié)果和我們設(shè)想一致,不過如果嘗試將18行的代碼注釋后再運行,雖然這行代碼只將運行時間延遲了兩秒,但一旦注釋便會引發(fā)錯誤,是什么原因呢?

向管道傳遞數(shù)據(jù)時,先讀的進程會把數(shù)據(jù)取走。簡言之,數(shù)據(jù)進入管道后成為無主數(shù)據(jù),也就是通過read函數(shù)先讀取數(shù)據(jù)的進程將得到數(shù)據(jù),即使該進程將數(shù)據(jù)傳到了管道。因此,注釋第18行將產(chǎn)生問題,在第19行,子進程將讀回自己在第17行向管道發(fā)送的數(shù)據(jù)。結(jié)果父進程調(diào)用read函數(shù)后將無限期等待數(shù)據(jù)進入管道。

從上述示例可以看到,只用一個管道進行雙向通信并非易事,為了簡化在進行雙向通信時,既然一個管道很難完成的任務(wù),不如就讓兩個管道來一起完成?因此創(chuàng)建兩個管道,各自負責不同的數(shù)據(jù)流動即可。其過程如圖1-4所示

 

TCP/IP網(wǎng)絡(luò)編程之進程與間通信

 

圖1-4 雙向通信模型2

由圖1-4可知,使用兩個管道可以解決單單通過一個管道來進行雙向通信的麻煩,下面采用上述模型來改進pipe2.c。

pipe3.c

  1. #include <stdio.h> 
  2. #include <unistd.h> 
  3. #define BUF_SIZE 30 
  4. int main(int argc, char *argv[]) 
  5. int fds1[2], fds2[2]; 
  6. char str1[] = "Who are you?"
  7. char str2[] = "Thank you for your message"
  8. char buf[BUF_SIZE]; 
  9. pid_t pid; 
  10. pipe(fds1), pipe(fds2); 
  11. pid = fork(); 
  12. if (pid == 0) 
  13. write(fds1[1], str1, sizeof(str1)); 
  14. read(fds2[0], buf, BUF_SIZE); 
  15. printf("Child proc output: %s \n", buf); 
  16. else 
  17. read(fds1[0], buf, BUF_SIZE); 
  18. printf("Parent proc output: %s \n", buf); 
  19. write(fds2[1], str2, sizeof(str2)); 
  20. sleep(3); 
  21. return 0; 
  22. }   
  • 第13行:創(chuàng)建兩個管道
  • 第17、33行:子進程可以通過數(shù)組fds1指向的管道向父進程傳輸數(shù)據(jù)
  • 第18、25行:父進程可以通過數(shù)組fds2指向的管道向子進程傳輸數(shù)據(jù)
  • 第26行:沒有太大的意義,只是為了延遲父進程終止的插入的代碼

編譯pipe3.c并運行

  1. # gcc pipe3.c -o pipe3 
  2. # ./pipe3 
  3. Parent proc output: Who are you? 
  4. Child proc output: Thank you for your message 

運用進程間通信

上一節(jié)學習了基于管道的進程間通信方法,接下來將其運用到網(wǎng)絡(luò)代碼中。如前所述,進程間通信與創(chuàng)建服務(wù)端并沒有直接關(guān)聯(lián),但有助于理解操作系統(tǒng)。

保存消息的回聲服務(wù)端

擴展TCP/IP網(wǎng)絡(luò)編程之多進程服務(wù)端(二)這一章的echo_mpserv.c,添加將回聲客戶端傳輸?shù)淖址葱虮4娴轿募小N覀兛梢詫⑦@個任務(wù)交給另外的進程,換言之,另行創(chuàng)建進程,從向客戶端服務(wù)的進程字符串信息。當然,該過程需要創(chuàng)建用于接收數(shù)據(jù)的管道。

下面給出示例,該示例可以與任意回聲客戶端配合運行,我們將用之前介紹過的echo_mpserv.c。

echo_storeserv.c

  1. #include <stdio.h> 
  2. #include <stdlib.h> 
  3. #include <string.h> 
  4. #include <unistd.h> 
  5. #include <signal.h> 
  6. #include <sys/wait.h> 
  7. #include <arpa/inet.h> 
  8. #include <sys/socket.h> 
  9. #define BUF_SIZE 100 
  10. void error_handling(char *message); 
  11. void read_childproc(int sig); 
  12. int main(int argc, char *argv[]) 
  13. int serv_sock, clnt_sock; 
  14. struct sockaddr_in serv_adr, clnt_adr; 
  15. int fds[2]; 
  16. pid_t pid; 
  17. struct sigaction act; 
  18. socklen_t adr_sz; 
  19. int str_len, state; 
  20. char buf[BUF_SIZE]; 
  21. if (argc != 2) 
  22. printf("Usage : %s <port>\n", argv[0]); 
  23. exit(1); 
  24. act.sa_handler = read_childproc; 
  25. sigemptyset(&act.sa_mask); 
  26. act.sa_flags = 0; 
  27. state = sigaction(SIGCHLD, &act, 0); 
  28. serv_sock = socket(PF_INET, SOCK_STREAM, 0); 
  29. memset(&serv_adr, 0, sizeof(serv_adr)); 
  30. serv_adr.sin_family = AF_INET; 
  31. serv_adr.sin_addr.s_addr = htonl(INADDR_ANY); 
  32. serv_adr.sin_port = htons(atoi(argv[1])); 
  33. if (bind(serv_sock, (struct sockaddr *)&serv_adr, sizeof(serv_adr)) == -1) 
  34. error_handling("bind() error"); 
  35. if (listen(serv_sock, 5) == -1) 
  36. error_handling("listen() error"); 
  37. pipe(fds); 
  38. pid = fork(); 
  39. if (pid == 0) 
  40. FILE *fp = fopen("echomsg.txt""wt"); 
  41. char msgbuf[BUF_SIZE]; 
  42. int i, len; 
  43. for (i = 0; i < 10; i++) 
  44. len = read(fds[0], msgbuf, BUF_SIZE); 
  45. fwrite((void *)msgbuf, 1, len, fp); 
  46. fclose(fp); 
  47. return 0; 
  48. while (1) 
  49. adr_sz = sizeof(clnt_adr); 
  50. clnt_sock = accept(serv_sock, (struct sockaddr *)&clnt_adr, &adr_sz); 
  51. if (clnt_sock == -1) 
  52. continue
  53. else 
  54. puts("new client connected..."); 
  55. pid = fork(); 
  56. if (pid == 0) 
  57. close(serv_sock); 
  58. while ((str_len = read(clnt_sock, buf, BUF_SIZE)) != 0) 
  59. write(clnt_sock, buf, str_len); 
  60. write(fds[1], buf, str_len); 
  61. close(clnt_sock); 
  62. puts("client disconnected..."); 
  63. return 0; 
  64. else 
  65. close(clnt_sock); 
  66. close(serv_sock); 
  67. return 0; 
  68. void read_childproc(int sig) 
  69. pid_t pid; 
  70. int status; 
  71. pid = waitpid(-1, &status, WNOHANG); 
  72. printf("removed proc id: %d \n", pid); 
  73. void error_handling(char *message) 
  74. fputs(message, stderr); 
  75. fputc('\n', stderr); 
  76. exit(1); 
  77. }    
  • 第47、48行:第47行創(chuàng)建管道,第48行創(chuàng)建負責保存文件的進程
  • 第49~62行:第49行創(chuàng)建的子進程運行區(qū)域,該區(qū)域從管道出口fds[0]讀取數(shù)據(jù)并保存到文件中。另外,上述服務(wù)端并不終止運行,而是不斷向客戶端提供服務(wù)。因此,數(shù)據(jù)在文件中累計到一定程序即關(guān)閉文件,該過程通過第55行的循環(huán)完成
  • 第80行:第73行通過fork函數(shù)創(chuàng)建的所有子進程將復制第47行創(chuàng)建的管道的文件描述符,因此,可以通過管道入口fds[1]傳遞字符串信息

編譯echo_storeserv.c并運行

  1. # gcc echo_storeserv.c -o echo_storeserv 
  2. # ./echo_storeserv 8500 
  3. new client connected... 
  4. new client connected... 
  5. client disconnected... 
  6. removed proc id: 8647 
  7. removed proc id: 8633 
  8. client disconnected... 
  9. removed proc id: 8644 

運行結(jié)果echo_mpclient ONE:

  1. # ./echo_mpclient 127.0.0.1 8500 
  2. Hello world! 
  3. Message from server: Hello world! 
  4. Hello Amy! 
  5. Message from server: Hello Amy! 
  6. Hello Tom! 
  7. Message from server: Hello Tom! 
  8. Hello Jack! 
  9. Message from server: Hello Jack! 
  10. Hello Rose! 
  11. Message from server: Hello Rose! 
  12. q   

運行結(jié)果echo_mpclient TWO:

  1. # ./echo_mpclient 127.0.0.1 8500 
  2. Hello Java! 
  3. Message from server: Hello Java! 
  4. Hello Python! 
  5. Message from server: Hello Python! 
  6. Hello Golang! 
  7. Message from server: Hello Golang! 
  8. Hello Spring! 
  9. Message from server: Hello Spring! 
  10. Hello Flask! 
  11. Message from server: Hello Flask! 

打印echomsg.txt文件

  1. # cat echomsg.txt 
  2. Hello world! 
  3. Hello Amy! 
  4. Hello Java! 
  5. Hello Python! 
  6. Hello Tom! 
  7. Hello Jack! 
  8. Hello Rose! 
  9. Hello Golang! 
  10. Hello Spring! 
  11. Hello Flask! 

如上運行結(jié)果所示,啟動多個客戶端向服務(wù)端傳輸數(shù)據(jù)時,文件中累計一定數(shù)量的字符串后(共調(diào)用十次fwrite函數(shù)),可以打開echomsg.txt存入字符串。

 

責任編輯:武曉燕 來源: 今日頭條
相關(guān)推薦

2017-08-06 00:05:18

進程通信開發(fā)

2015-04-24 09:48:59

TCPsocketsocket編程

2021-01-22 10:58:16

網(wǎng)絡(luò)安全進程間碼如

2010-01-05 10:00:48

Linux進程間通信

2011-06-22 17:09:50

QT 進程 通信

2020-11-04 07:17:42

Nodejs通信進程

2020-11-18 09:06:04

Python

2013-03-28 13:14:45

AIDL進程間通信Android使用AI

2018-01-12 14:35:00

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

2017-06-19 13:36:12

Linux進程消息隊列

2011-08-08 10:02:55

iPhone開發(fā) 進程 通信

2024-01-05 08:41:31

進程間通信IPC異步通信

2021-02-14 21:05:05

通信消息系統(tǒng)

2020-09-22 07:35:06

Linux線程進程

2019-05-08 11:10:05

Linux進程語言

2018-05-30 13:58:02

Linux進程通信

2011-06-24 14:01:34

Qt QCOP 協(xié)議

2020-04-29 15:10:16

Linux命令進程

2017-01-10 13:39:57

Python線程池進程池

2019-09-18 20:07:06

AndroidTCP協(xié)議
點贊
收藏

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