文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

TCP/IP网络编程之进程与进程间通信

2024-12-24 21:21

关注

进程间通信基本概念

进程间通信意味着两个不同进程间可以交换数据,为了完成这一点,操作系统中应提供两个进程可以同时访问的内存空间。但我们知道,进程具有完全独立的内存结构,就连通过fork函数创建的子进程也不会和父进程共享内存,因此,进程间通信只能通过其他特殊方法完成。

基于管道实现进程间通信

图1-1表示基于管道(PIPE)的进程间通信结构模型

 

 

图1-1 基于管道的进程间通信模型

从图1-1可以看到,为了完成进程间通信,需要创建管道。管道并非属于进程资源,而是和套接字一样,属于操作系统资源(也就不是fork函数的复制对象)。下面介绍创建管道函数

  1. #include 
  2. int pipe (int filedes[2]);//成功时返回0,失败时返回-1 

以长度为2的int数组地址值作为参数调用上述函数时,数组中存有两个文件描述符,它们将被用作管道的出口和入口。父进程调用该函数时将创建管道,同时获取对应于出入口的文件描述符,此时父进程可以读写同一管道。但父进程的目的是与子进程进行数据交换,因此需要将入口和出口中的一个文件描述符传递给子进程,如何完成传递呢?答案还是调用fork函数。

  1. pipe1.c 
  2. #include  
  3. #include  
  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; 

编译pipe1.c并运行

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

上述示例中的通信方法及路径如图1-2所示,重点在于,父子进程都可以访问管道的I/O路径,但子进程仅用输入路径,父进程仅用输出路径

 

 

图1-2 示例pipe1.c的通信路径

以上就是管道的基本原理及通信方法,应用管道时还有一部分内容需要注意,通过双向通信示例进一步说明

通过管道进行进程间双向通信

下面创建两个进程通过一个管道进行双向数据交换的示例,其通信方式如图1-3所示

 

 

图1-3 双向通信模型1

从图1-3可以看出,通过一个管道可以进行双向通信,但采用这种模型需格外小心,先给出示例,稍后再讨论。

pipe2.c

  1. #include  
  2. #include  
  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; 

编译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 

运行结果和我们设想一致,不过如果尝试将18行的代码注释后再运行,虽然这行代码只将运行时间延迟了两秒,但一旦注释便会引发错误,是什么原因呢?

向管道传递数据时,先读的进程会把数据取走。简言之,数据进入管道后成为无主数据,也就是通过read函数先读取数据的进程将得到数据,即使该进程将数据传到了管道。因此,注释第18行将产生问题,在第19行,子进程将读回自己在第17行向管道发送的数据。结果父进程调用read函数后将无限期等待数据进入管道。

从上述示例可以看到,只用一个管道进行双向通信并非易事,为了简化在进行双向通信时,既然一个管道很难完成的任务,不如就让两个管道来一起完成?因此创建两个管道,各自负责不同的数据流动即可。其过程如图1-4所示

 

 

图1-4 双向通信模型2

由图1-4可知,使用两个管道可以解决单单通过一个管道来进行双向通信的麻烦,下面采用上述模型来改进pipe2.c。

pipe3.c

  1. #include  
  2. #include  
  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. }   

编译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 

运用进程间通信

上一节学习了基于管道的进程间通信方法,接下来将其运用到网络代码中。如前所述,进程间通信与创建服务端并没有直接关联,但有助于理解操作系统。

保存消息的回声服务端

扩展TCP/IP网络编程之多进程服务端(二)这一章的echo_mpserv.c,添加将回声客户端传输的字符串按序保存到文件中。我们可以将这个任务交给另外的进程,换言之,另行创建进程,从向客户端服务的进程字符串信息。当然,该过程需要创建用于接收数据的管道。

下面给出示例,该示例可以与任意回声客户端配合运行,我们将用之前介绍过的echo_mpserv.c。

echo_storeserv.c

  1. #include  
  2. #include  
  3. #include  
  4. #include  
  5. #include  
  6. #include  
  7. #include  
  8. #include  
  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 \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. }    

编译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 

运行结果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   

运行结果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! 

如上运行结果所示,启动多个客户端向服务端传输数据时,文件中累计一定数量的字符串后(共调用十次fwrite函数),可以打开echomsg.txt存入字符串。

 

来源:今日头条内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯