当前位置:   article > 正文

1.Linux——进程间的五种通信方式(管道、消息队列、共享存储、信号、信号量)_linux下两个进程之间通信的方法

linux下两个进程之间通信的方法

文章目录

引言

        进程间通信(IPC,InterProcess Communication)是指在不同的进程之间传播或交换信息。       

        前7种IPC通常限于同一台主机的各个进程间的IPC。然而套接字和STREAMS,是仅有的两种支持不同主机上各个进程间IPC的类型。

1.管道

        管道是UNIX系统IPC的最古老形式,并且所有UNIX系统都提供此种通信机制。

        管道的特点(也是两种局限性):

  1. 历史上,它们是半双工的(即数据只在一个方向上流动)。具有固定的读端与写端。现在,有些系统提供了全双工管道,但是为了最佳的可移植性,我们决不预先假定系统使用此特性。
  2. 只能在具有亲缘关系之间的通信(父子或兄弟之间)。             

       

 1.1 无名管道

  1. 无名管道特点:
    • 它是半双工的(即数据只能在一个方向上流动),具有固定的读端和写端。
    • 只能在具有亲缘关系之间的通信
    • 管道是创建在内存中(只存在于内存中),进程结束空间释放,管道不复存在。对于它的读写可以使用普通的read、write等函数.(管道中的数据,读走就没有了)
  2. 函数原型

        经由参数filedes返回两个文件描述符:filedes[0]为读而打开,filedes[1]为写而打开。filedes[1]的输出是filedes[0]的输人。

         调用fork之后做什么取决于我们想要有的数据流的方向。对于从父进程到子进程的管道,父进程关闭管道的读端(fd[0]),子进程则关闭写端(fd[1])。图15-3显示了在此之后描述符的安排。

         为了构造从子进程到父进程的管道,父进程关闭fd[1](写端),子进程关闭fd[0](读端)。 记忆:通常喜欢念0 1对应这里的 读 写。

        3.例子:

        数据流从父进程流入子进程,父进程打开写端,关闭读端。子进程则是打开读端,关闭写端。反之,如果数据流从子进程流向父进程也是一样。

  1. #include <stdio.h>
  2. #include <unistd.h>
  3. #include <string.h>
  4. #include <stdlib.h>
  5. int main()
  6. {
  7. int fd[2];
  8. int pid;
  9. char buf[128];
  10. // int pipe(int pipefd[2]);
  11. if(pipe(fd) == -1){
  12. printf("creat pipe failed\n");
  13. }
  14. pid = fork();
  15. if(pid<0){
  16. printf("creat child failed\n");
  17. }
  18. else if(pid > 0){
  19. sleep(3);
  20. printf("this is father\n");
  21. close(fd[0]);
  22. write(fd[1],"hello from father",strlen("hello form father"));
  23. wait();
  24. }else{
  25. printf("this is child\n");
  26. close(fd[1]);
  27. read(fd[0],buf,128);
  28. printf("read from father: %s\n",buf);
  29. exit(0);
  30. }
  31. return 0;
  32. }

运行效果:

 1.2 命名管道(FIFO)     

  1. 特点:
    1. 命名管道可以在无关的进程之间交换数据,与无名管道不同。(通过FIFO,不相关的进程也能交换数据)
    2. 命名管道有路径名与之相关联,它以一种特殊设备文件形式存在于文件系统中。(创建FIFO类似于创建文件。确实,FIFO的路径名存在于文件系统中。)
  2. 函数原型:

          一旦已经用mkfifo创建了一个FIFO,就可用open打开它。其实,一般的文件I/O函数(close、read、write、unlink等)都可用于FIFO。

        FIFO的通信方式类似于在进程中使用文件来传输数据,只不过FIFO类型文件同时具有管道的特性。在数据读出时,FIFO管道中同时清楚数据,并且”先进先出“。

    3.  FIFO有下面两种用途;
        (1) FIFO由shell命令使用以便将数据从一条管道线传送到另一条,为此无需创建中间临时文件。
        (2) FIFO用于客户进程-服务器进程应用程序中,以在客户进程和服务器进程之间传递数据。我们各用一个例子来说明这两种用途。

代码段:

        先运行 read.c,再运行 write.c ;运行之后读端先阻塞起来等待写端的写入,写入完之后读端开始运行。

        读端代码块:

  1. #include <sys/types.h>
  2. #include <sys/stat.h>
  3. #include <stdio.h>
  4. #include <errno.h>
  5. #include <fcntl.h>
  6. //int mkfifo(const char *pathname, mode_t mode);
  7. int main()
  8. {
  9. char buf[30] = {0};
  10. int nread = 0;
  11. if( (mkfifo("./file",0600) == -1) && errno!=EEXIST){
  12. printf("mkfifo failuer\n");
  13. perror("why");
  14. }
  15. int fd = open("./file",O_RDONLY);
  16. printf("open success\n");
  17. while(1){
  18. nread = read(fd,buf,30);
  19. printf("read %d byte from fifo,context:%s\n",nread,buf);
  20. }
  21. close(fd);
  22. return 0;
  23. }

        写端代码块:

  1. #include <sys/types.h>
  2. #include <sys/stat.h>
  3. #include <stdio.h>
  4. #include <errno.h>
  5. #include <fcntl.h>
  6. #include <string.h>
  7. //int mkfifo(const char *pathname, mode_t mode);
  8. int main()
  9. {
  10. int cnt = 0;
  11. char *str = "message from fifo";
  12. int fd = open("./file",O_WRONLY);
  13. printf("write open success\n");
  14. while(1){
  15. write(fd, str, strlen(str));
  16. sleep(1);
  17. if(cnt == 5){
  18. break;
  19. }
  20. }
  21. close(fd);
  22. return 0;
  23. }

2.消息队列

        消息队列是消息的链接表存放在内核中并由消息队列标识符标识。

        2.1 特点:       

  • 消息队列是面向记录的,其中的消息具有特地那个的格式以及特定的优先级。
  • 消息队列独立于发送与接收进程。进程终止时,消息队列及内容并不会删除
  • 消息队列可以实现信息的随机查询,消息不一定要以先进先出的次序读取,也可以按消息的类型读取。

        2.2 函数原型:

 

        从队列中取消息,并不一定要以先进先出次序,也可以按信息的类型字段取消息。 

         函数msgget中key值很关键,相当于一个索引。进程通过这个键值,在Linux内核中找到相应的队列(确保了队列是同一个) 在以下两种情况中,msgget 将创建一个新的消息队列:

  • 如果没有与键值相对应的消息队列,并且flag中包含了 IPC_CREAT 标志位。
  • key 参数为 IPC_PRIVATE。

        函数msgrcv在读取消息队列时,type参数有下面几种情况:

  • type == 0 ,返回队列中的第一个消息;
  •  type > 0 ,返回队列中消息类型为 type 的第一个消息;
  • type < 0 ,返回队列中消息类型值小于或等于 type 绝对值的消息,如果有多个,则取类型值最小的消息。
     

代码端:

  1. #include <stdio.h>
  2. #include <sys/types.h>
  3. #include <sys/ipc.h>
  4. #include <sys/msg.h>
  5. #include <string.h>
  6. // int msgget(key_t key, int msgflg);
  7. // int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
  8. // ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp,
  9. // int msgflg);
  10. struct msgbuf {
  11. long mtype; /* message type, must be > 0 */
  12. char mtext[256]; /* message data */
  13. };
  14. int main()
  15. {
  16. //1.huoqu
  17. struct msgbuf readBuf;
  18. //获取key值
  19. key_t key;
  20. key = ftok(".",'m');// “.” 表示当前路径,第二个参数随意,可以是数字,可以是字符
  21. printf("key=%x\n",key);
  22. // 获取/创建消息队列
  23. int msgId = msgget(key, IPC_CREAT|0777);//0777 可读可写可执行
  24. if(msgId == -1 ){
  25. printf("get que failuer\n");
  26. }
  27. memset(&readBuf,0,sizeof(struct msgbuf));
  28. //接收数据
  29. msgrcv(msgId, &readBuf,sizeof(readBuf.mtext),888,0); //第三个参数:消息类型,第四个参数:0 的意思是默认的方式接收消息,接收不到消息会一直阻塞
  30. printf("read from que:%s\n",readBuf.mtext);
  31. //发送数据
  32. struct msgbuf sendBuf = {988,"thank you for reach"};
  33. msgsnd(msgId,&sendBuf,strlen(sendBuf.mtext),0);
  34. //关闭数据
  35. msgctl(msgId,IPC_RMID,NULL);//第三个参数,通常设置为NULL
  36. return 0;
  37. }
  1. #include <stdio.h>
  2. #include <sys/types.h>
  3. #include <sys/ipc.h>
  4. #include <sys/msg.h>
  5. #include <string.h>
  6. // int msgget(key_t key, int msgflg);
  7. // int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
  8. // ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp,
  9. // int msgflg);
  10. struct msgbuf {
  11. long mtype; /* message type, must be > 0 */
  12. char mtext[256]; /* message data */
  13. };
  14. int main()
  15. {
  16. //1.huoqu
  17. struct msgbuf sendBuf = {888,"this is message from quen"}; //第一个参数:消息的类型
  18. struct msgbuf readBuf;
  19. memset(&readBuf,0,sizeof(struct msgbuf));
  20. //获取key值
  21. key_t key;
  22. key = ftok(".",'m');// “.” 表示当前路径
  23. printf("key=%x\n",key);
  24. //创建消息队列
  25. int msgId = msgget(key, IPC_CREAT|0777);//0777 可读可写可执行
  26. if(msgId == -1 ){
  27. printf("get que failuer\n");
  28. }
  29. //发送数据
  30. msgsnd(msgId,&sendBuf,strlen(sendBuf.mtext),0);
  31. printf("send over\n");
  32. //接收数据
  33. msgrcv(msgId, &readBuf,sizeof(readBuf.mtext),988,0);//第四个参数:0 的意思是默认的方式接收消息,接收不到消息会一直阻塞
  34. printf("reaturn from get:%s\n",readBuf.mtext);
  35. //关闭消息队列
  36. msgctl(msgId,IPC_RMID,NULL);
  37. return 0;
  38. }

运行效果:

3.共享存储

        共享存储允许两个或更多进程共享给定的存储区。因为数据不需要在客户进程和服务器进程之间复制,所以这是最快的一种IPC。

  1.  特点:
    1. 共享存储是最快的一种IPC,因为进程是直接对内存进行存储,而不需要任何数据的拷贝。
    2. 它有一个特性:只能单独一个进程写或读,如果A和B进程同时写,会造成数据的混乱,(所以需要搭配信号量来使用,比较好)

        2. 函数原型

  1. //创建或获取一个共享内存:成功返回共享内存ID,失败返回 -1
  2. int shmget(key_t key, size_t size, int shmflg);
  3. //连接共享内存到当前进程的地址空间:成功返回指向共享内存的指针,失败返回 -1
  4. void *shmat(int shmid, const void *shmaddr, int shmflg);
  5. //断开与共享内存的连接:成功返回0,失败返回 -1
  6. int shmdt(const void *shmaddr);
  7. //控制共享内存的相关信息:成功返回0,失败返回 -1
  8. int shmctl(int shmid, int cmd, struct shmid_ds *buf);

部分参数解释

 代码段:

        编程思路:          

  1. 创建/打开共享存储
  2. 连接映射共享存储
  3. 写入数据 strcpy
  4. 断开释放共享存储
  5. 干掉共享存储

 shm_write

  1. #include <sys/ipc.h>
  2. #include <sys/shm.h>
  3. #include <stdlib.h>
  4. #include <stdio.h>
  5. #include <string.h>
  6. //int shmget(key_t key, size_t size, int shmflg);
  7. int main()
  8. {
  9. int shmid;
  10. char *shmaddr;
  11. key_t key;
  12. key = ftok(".",1);
  13. shmid = shmget(key,1024*4,IPC_CREAT|0666);
  14. if(shmid == -1){
  15. printf("shmget noOk\n");
  16. exit(-1);
  17. }
  18. shmaddr = shmat(shmid,0,0);
  19. printf("shmat ok\n");
  20. strcpy(shmaddr,"chenlichen");
  21. sleep(5);
  22. shmdt(shmaddr);
  23. shmctl(shmid, IPC_RMID, 0);
  24. printf("quit\n");
  25. return 0;
  26. }

 shm_get

  1. #include <sys/ipc.h>
  2. #include <sys/shm.h>
  3. #include <stdlib.h>
  4. #include <stdio.h>
  5. #include <string.h>
  6. //int shmget(key_t key, size_t size, int shmflg);
  7. int main()
  8. {
  9. int shmid;
  10. char *shmaddr;
  11. key_t key;
  12. key = ftok(".",1);
  13. shmid = shmget(key,1024*4,0);
  14. if(shmid == -1){
  15. printf("shmget noOk\n");
  16. exit(-1);
  17. }
  18. shmaddr = shmat(shmid,0,0);
  19. printf("shmat ok\n");
  20. printf("data: %s\n:",shmaddr);
  21. shmdt(shmaddr);
  22. printf("quit\n");
  23. return 0;
  24. }

 运行效果:

 

前3种通信方式概况图:

4.信号

2.Linux——信号_单片机爱好者之家的博客-CSDN博客

5.信号量

6.Linux——信号量_单片机爱好者之家的博客-CSDN博客

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/2023面试高手/article/detail/256289
推荐阅读
相关标签
  

闽ICP备14008679号