加入收藏 | 设为首页 | 会员中心 | 我要投稿 江门站长网 (https://www.0750zz.com/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 服务器 > 搭建环境 > Windows > 正文

Linux下的进程间通信:使用管道和消息队列

发布时间:2019-05-14 07:13:24 所属栏目:Windows 来源:Marty Kalin
导读:副标题#e# 学习在 Linux 中进程是如何与其他进程进行同步的。 本篇是 Linux 下进程间通信(IPC)系列的第二篇文章。第一篇文章 聚焦于通过共享文件和共享内存段这样的共享存储来进行 IPC。这篇文件的重点将转向管道,它是连接需要通信的进程之间的通道。管

在父进程中的 else 子句将会关闭管道的读端:

  1. close(pipeFDs[ReadEnd]); /* called in parent code */

然后父进程将向无名管道中写入某些字节数据(ASCII 代码),子进程读取这些数据,然后向标准输出中回放它们。

在这个程序中还需要澄清的一点是在父进程代码中的 wait 函数。一旦被创建后,子进程很大程度上独立于它的父进程,正如简短的 pipeUN 程序所展示的那样。子进程可以执行任意的代码,而它们可能与父进程完全没有关系。但是,假如当子进程终止时,系统将会通过一个信号来通知父进程。

要是父进程在子进程之前终止又该如何呢?在这种情形下,除非采取了预防措施,子进程将会变成在进程表中的一个僵尸进程。预防措施有两大类型:第一种是让父进程去通知系统,告诉系统它对子进程的终止没有任何兴趣:

  1. signal(SIGCHLD, SIG_IGN); /* in parent: ignore notification */

第二种方法是在子进程终止时,让父进程执行一个 wait。这样就确保了父进程可以独立于子进程而存在。在 pipeUN 程序中使用了第二种方法,其中父进程的代码使用的是下面的调用:

  1. wait(NULL); /* called in parent */

这个对 wait 的调用意味着一直等待直到任意一个子进程的终止发生,因此在 pipeUN 程序中,只有一个子进程。(其中的 NULL 参数可以被替换为一个保存有子程序退出状态的整数变量的地址。)对于更细粒度的控制,还可以使用更灵活的 waitpid 函数,例如特别指定多个子进程中的某一个。

pipeUN 将会采取另一个预防措施。当父进程结束了等待,父进程将会调用常规的 exit 函数去退出。对应的,子进程将会调用 _exit 变种来退出,这类变种将快速跟踪终止相关的通知。在效果上,子进程会告诉系统立刻去通知父进程它的这个子进程已经终止了。

假如两个进程向相同的无名管道中写入内容,字节数据会交错吗?例如,假如进程 P1 向管道写入内容:

  1. foo bar

同时进程 P2 并发地写入:

  1. baz baz

到相同的管道,最后的结果似乎是管道中的内容将会是任意错乱的,例如像这样:

  1. baz foo baz bar

只要没有写入超过 PIPE_BUF 字节,POSIX 标准就能确保写入不会交错。在 Linux 系统中, PIPE_BUF 的大小是 4096 字节。对于管道我更喜欢只有一个写入方和一个读取方,从而绕过这个问题。

命名管道

无名管道没有备份文件:系统将维持一个内存缓存来将字节数据从写方传给读方。一旦写方和读方终止,这个缓存将会被回收,进而无名管道消失。相反的,命名管道有备份文件和一个不同的 API。

下面让我们通过另一个命令行示例来了解命名管道的要点。下面是具体的步骤:

  • 开启两个终端。这两个终端的工作目录应该相同。
  • 在其中一个终端中,键入下面的两个命令(命令行提示符仍然是 %,我的注释以 ## 打头。):

    1. % mkfifo tester ## 创建一个备份文件,名为 tester
    2. % cat tester ## 将管道的内容输出到 stdout

    在最开始,没有任何东西会出现在终端中,因为到现在为止没有在命名管道中写入任何东西。

  • 在第二个终端中输入下面的命令:

    1. % cat > tester ## redirect keyboard input to the pipe
    2. hello, world! ## then hit Return key
    3. bye, bye ## ditto
    4. <Control-C> ## terminate session with a Control-C

    无论在这个终端中输入什么,它都会在另一个终端中显示出来。一旦键入 Ctrl+C,就会回到正常的命令行提示符,因为管道已经被关闭了。

  • 通过移除实现命名管道的文件来进行清理:

    1. % unlink tester

正如 mkfifo 程序的名字所暗示的那样,命名管道也被叫做 FIFO,因为第一个进入的字节,就会第一个出,其他的类似。有一个名为 mkfifo 的库函数,用它可以在程序中创建一个命名管道,它将在下一个示例中被用到,该示例由两个进程组成:一个向命名管道写入,而另一个从该管道读取。

示例 2. fifoWriter 程序

  1. #include <sys/types.h>
  2. #include <sys/stat.h>
  3. #include <fcntl.h>
  4. #include <unistd.h>
  5. #include <time.h>
  6. #include <stdlib.h>
  7. #include <stdio.h>
  8.  
  9. #define MaxLoops 12000 /* outer loop */
  10. #define ChunkSize 16 /* how many written at a time */
  11. #define IntsPerChunk 4 /* four 4-byte ints per chunk */
  12. #define MaxZs 250 /* max microseconds to sleep */
  13.  
  14. int main() {
  15. const char* pipeName = "./fifoChannel";
  16. mkfifo(pipeName, 0666); /* read/write for user/group/others */
  17. int fd = open(pipeName, O_CREAT | O_WRONLY); /* open as write-only */
  18. if (fd < 0) return -1; /** error **/
  19. int i;
  20. for (i = 0; i < MaxLoops; i++) { /* write MaxWrites times */
  21. int j;
  22. for (j = 0; j < ChunkSize; j++) { /* each time, write ChunkSize bytes */
  23. int k;
  24. int chunk[IntsPerChunk];
  25. for (k = 0; k < IntsPerChunk; k++)
  26. chunk[k] = [rand][9]();
  27. write(fd, chunk, sizeof(chunk));
  28. }
  29. usleep(([rand][9]() % MaxZs) + 1); /* pause a bit for realism */
  30. }
  31.  
  32. close(fd); /* close pipe: generates an end-of-file */
  33. unlink(pipeName); /* unlink from the implementing file */
  34. [printf][10]("%i ints sent to the pipe.n", MaxLoops * ChunkSize * IntsPerChunk);
  35.  
  36. return 0;
  37. }

上面的 fifoWriter 程序可以被总结为如下:

  • (编辑:江门站长网)

    【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!