0


【Linux C | 进程】Linux 进程间通信的10种方式(2)

😁博客主页😁:🚀https://blog.csdn.net/wkd_007🚀
🤑博客内容🤑:🍭嵌入式开发、Linux、C语言、C++、数据结构、音视频🍭
🤣本文内容🤣:🍭介绍 🍭
😎金句分享😎:🍭你不能选择最好的,但最好的会来选择你——泰戈尔🍭

本文未经允许,不得转发!!!

目录


在这里插入图片描述

🎄一、POSIX消息队列

✨1.1 POSIX消息队列介绍

POSIX消息队列与System V消息队列有一定的相似之处, 信息交换的基本单位是消息, 但也有显著的区别。Linux实现里POSIX消息队列的句柄本质是文件描述符,所以可以使用I/O多路复用系统调用(select、 poll或epoll
等) 来监控这个文件描述符。其次, POSIX消息队列提供了通知功能, 当消息队列中有消息可用时, 就会通知到进程。

编程步骤:

  • 1、创建/获取消息队列#include<fcntl.h>/* For O_* constants */#include<sys/stat.h>/* For mode constants */#include<mqueue.h>mqd_tmq_open(constchar*name,int oflag);mqd_tmq_open(constchar*name,int oflag,mode_t mode,structmq_attr*attr); name:必须以/打头, 而且后续字符不允许出现/,最大长度为255个字符; oflag:允许的标志位包括O_RDONLY、O_WRONLY、O_RDWR、O_CREAT、O_EXCL、O_NONBLOCK mode:mode设置的是访问权限,创建时有效; attr:attr设置的是消息队列的属性,创建时有效。int mq_fd =mq_open("/mqPosix", O_RDWR | O_CREAT,0666,NULL);//创建int mq_fd =mq_open("/mqPosix", O_RDWR);// 获取
  • 2、发送/获取数据 服务端发送:mq_send(mq_fd, buf, strlen(buf), i); 客户端获取:mq_receive(mq_fd, buf, mqAttr.mq_msgsize, &prio); 获取数据之前,可能需要先获取消息队列的属性,mq_getattr(mq_fd,&attr)
  • 3、关闭消息队列句柄mq_close(mq_fd);
  • 4、删除消息队列mq_unlink("/mqPosix");

✨1.2 例子

#include<stdio.h>#include<stdlib.h>#include<unistd.h>#include<string.h>#include<mqueue.h>#include<errno.h>#include<fcntl.h>#include<sys/stat.h>intmain(){// 创建子进程pid_t pid =fork();if(pid ==0){// 子进程printf("子进程[%d]开始执行, 创建POSIX消息队列,循环往里写数据\n",getpid());// 创建消息队列int mq_fd =mq_open("/mqPosix", O_RDWR|O_CREAT|O_EXCL,0664,NULL);if(mq_fd <0){if(errno == EEXIST){printf("/mqPosix EEXIST\n");mq_unlink("/mqPosix");
                mq_fd =mq_open("/mqPosix", O_RDWR | O_CREAT,0666,NULL);}else{perror("mq_open failed");exit(-1);}}// 发送数据int i =9;while(i>=0){char buf[256]={0,};sprintf(buf,"hello-%d", i);if(mq_send(mq_fd, buf,strlen(buf), i)<0){perror("mq_send failed");exit(-1);}printf("子进程[%d]写入数据:hello-%d\n",getpid(), i);
            i--;sleep(1);}mq_close(mq_fd);printf("子进程[%d]退出\n",getpid());return0;}elseif(pid >0)// 父进程{sleep(3);//延时一会,让子进程先运行printf("父进程[%d]开始执行, 获取消息队列,读取数据\n",getpid());int mq_fd =mq_open("/mqPosix", O_RDWR);if(mq_fd ==-1){perror("mq_open failed");exit(1);}structmq_attr mqAttr;mq_getattr(mq_fd,&mqAttr);printf("mq_msgsize=%ld\n",mqAttr.mq_msgsize);char*buf =(char*)malloc(mqAttr.mq_msgsize);while(1){unsigned prio =0;int res =mq_receive(mq_fd, buf, mqAttr.mq_msgsize,&prio);//阻塞printf("res=%d, 消息:%s, prio:%u\n", res, buf, prio);if(res ==-1){perror("mq_receive failed");break;}}mq_close(mq_fd);mq_unlink("/mqPosix");printf("父进程[%d]退出\n",getpid());return0;}else{printf("Error in fork\n");exit(1);}return0;}

保存上面代码,运行

gcc mq_posix.c -lrt

运行结果:
在这里插入图片描述

在这里插入图片描述

🎄二、POSIX信号量

✨2.1 POSIX信号量介绍

POSIX信号量和System V信号量的作用是相同的, 都是用于同步进程之间及线程之间的操作, 以达到无冲突地访问共享资源的目的。

POSIX提供了两类信号量: 有名信号量和无名信号量。
无名信号量, 又称为

基于内存的信号量

, 由于其没有名字, 没法通过open操作直接找到对应的信号量, 所以很难直接用于没有关联的两个进程之间。 无名信号量多用于

线程之间的同步


有名信号量由于其有名字, 多个不相干的进程

可以通过名字来打开同一个信号量

, 从而完成同步操作, 所以有名信号量的操作要方便一些, 适用范围也比无名信号量更广。

有名信号量编程步骤:

  • 1、创建/获取有名信号量#include<fcntl.h>#include<sys/stat.h>#include<semaphore.h>sem_t*sem_open(constchar*name,int oflag);sem_t*sem_open(constchar*name,int oflag,mode_t mode,unsignedint value); name:可以以1个或多个/打头, 也可以不以/打头。打头的一个或多个/字符不计入长度。最大长度为255个字符; oflag:oflag标志位支持的标志包括O_CREAT和O_EXCL标志位。 如果带了O_CREAT标志位,则表示要创建信号量; mode:mode设置的是访问权限,创建时有效; value:value是新建信号量的初始值,创建时有效。sem_t*sem_p =sem_open("/semPosix", O_RDWR|O_CREAT|O_EXCL,0664,1);//创建sem_t*sem_p =sem_open("/semPosix", O_RDWR);// 获取
  • 2、申请该资源 当申请该资源时, 需要先调用sem_wait函数; 当发布该资源或使用完毕释放该资源时,则调用sem_post函数。// 使用资源,数量 -1sem_wait(sem_p);// 使用资源...// 释放资源,数量 +1sem_post(sem_p); 可能还有使用下面两个等待信号量的函数:intsem_trywait(sem_t*sem);intsem_timedwait(sem_t*sem,conststructtimespec*abs_timeout);
  • 3、关闭信号量句柄sem_close(sem_p);
  • 4、删除信号量sem_close(sem_p);

无名信号量编程步骤和上面的基本差不多,就创建和销毁不一样:

  • 初始化无名信号量#include<semaphore.h>intsem_init(sem_t*sem,int pshared,unsignedint value);
  • 销毁无名信号量#include<semaphore.h>intsem_destroy(sem_t*sem);

✨2.2 例子

// gcc sem_posix.c -lpthread#include<stdio.h>#include<stdlib.h>#include<unistd.h>#include<string.h>#include<fcntl.h>#include<sys/stat.h>#include<semaphore.h>#include<errno.h>#include<sys/stat.h>intmain(){// 2 创建子进程pid_t pid =fork();if(pid ==0){// 子进程printf("子进程[%d]开始执行, 创建信号量,使用资源\n",getpid());// 创建信号量集sem_t*sem_p =sem_open("/semPosix", O_RDWR|O_CREAT|O_EXCL,0664,1);if(SEM_FAILED == sem_p){if(errno == EEXIST){printf("/semPosix EEXIST\n");sem_unlink("/semPosix");
                sem_p =sem_open("/semPosix", O_RDWR | O_CREAT,0666,1);}else{perror("sem_open failed");exit(-1);}}// 使用资源,数量 -1sem_wait(sem_p);printf("子进程[%d]访问共享资源\n",getpid());sleep(20);printf("子进程[%d]完成共享资源的访问\n",getpid());// 释放资源,数量 +1sem_post(sem_p);sem_close(sem_p);printf("子进程[%d]退出\n",getpid());return0;}elseif(pid >0)// 父进程{sleep(3);//延时一会,让子进程先运行printf("父进程[%d]开始执行, 获取信号量,准备使用资源\n",getpid());sem_t*sem_p =sem_open("/semPosix", O_RDWR);if(SEM_FAILED == sem_p){perror("sem_open failed");exit(1);}// 使用资源,数量 -1sem_wait(sem_p);printf("父进程[%d]访问共享资源\n",getpid());sleep(3);printf("父进程[%d]完成共享资源的访问\n",getpid());// 释放资源,数量 +1sem_post(sem_p);sem_close(sem_p);sem_unlink("/semPosix");printf("父进程[%d]退出\n",getpid());return0;}else{printf("Error in fork\n");exit(1);}return0;}

注意,编译时需要加

-lpthread

,保持上面代码,运行

gcc sem_posix.c -lpthread

编译,
运行结果如下:
在这里插入图片描述


在这里插入图片描述

🎄三、 POSIX共享内存

✨3.1 POSIX共享内存介绍

Linux系统中,POSIX共享内存的本质是将一个文件通过mmap函数将共享内存映射到进程的地址空间。

编程步骤:

  • 1、创建/获取共享内存句柄#include<sys/mman.h>#include<sys/stat.h>#include<fcntl.h>intshm_open(constchar*name,int oflag,mode_t mode); name:可以以1个或多个/打头, 也可以不以/打头。打头的一个或多个/字符不计入长度。最大长度为255个字符; oflag:oflag标志位要包含O_RDONLY或O_RDWR,另外,可以选择的标志位还有O_CREAT(表示创建) 、 O_EXCL(配合O_CREAT表示排他创建)、O_TRUNC(表示将共享内存的size截断成0)。 mode:mode设置的是访问权限;int shmid =shm_open("/shmPosix", O_RDWR|O_CREAT|O_EXCL,0666);//创建int shmid =shm_open("/shmPosix", O_RDWR,0666);// 获取 创建时,需要调用ftruncate(shmid, SHM_SIZE);调整共享内存文件大小。
  • 2、映射共享内存,得到虚拟地址 使用mmap函数映射共享内存,得到虚拟地址后,可以直接操作。void*p =mmap(NULL, SHM_SIZE, PROT_WRITE|PROT_READ, MAP_SHARED, shmid,0);
  • 3、使用后,按需解除映射munmap(p, SHM_SIZE);
  • 4、销毁共享内存shm_unlink("/shmPosix")

✨3.2 例子

#include<stdio.h>#include<stdlib.h>#include<unistd.h>#include<string.h>#include<sys/mman.h>#include<sys/stat.h>#include<fcntl.h>#defineSHM_SIZE8192intmain(){// 创建子进程pid_t pid =fork();if(pid ==0){// 子进程printf("子进程[%d]开始执行, 创建共享内存段,使用创建共享内存\n",getpid());// 2.1 创建共享内存段int shmid =shm_open("/shmPosix", O_RDWR|O_CREAT|O_EXCL,0666);if(shmid ==-1){perror("shm_open failed");exit(1);}ftruncate(shmid, SHM_SIZE);// 调整文件大小为 8192, 最好为页的整数倍// 2.2 映射共享内存,得到虚拟地址void*p =mmap(NULL, SHM_SIZE, PROT_WRITE|PROT_READ, MAP_SHARED, shmid,0);if((void*)MAP_FAILED == p){perror("mmap failed");exit(2);}// 2.3 读写共享内存int*pi = p;*pi =0xaaaaaaaa;*(pi+1)=0x55555555;printf("子进程[%d]写入%x, %x\n",getpid(),*pi,*(pi+1));// 2.4 解除映射if(munmap(p, SHM_SIZE)==-1){perror("munmap failed");exit(3);}printf("子进程[%d]解除映射, 结束进程\n\n",getpid());return0;}elseif(pid >0)// 父进程{sleep(3);//延时一会,让子进程先运行printf("父进程[%d]开始执行, 获取共享内存段,准备使用资源\n",getpid());// 3.1 获取共享内存段int shmid =shm_open("/shmPosix", O_RDWR,0666);if(shmid ==-1){perror("shm_open failed");exit(1);}// 3.2 映射共享内存,得到虚拟地址void*p =mmap(NULL, SHM_SIZE, PROT_WRITE|PROT_READ, MAP_SHARED, shmid,0);if((void*)MAP_FAILED == p){perror("mmap failed");exit(2);}// 3.3 读写共享内存int x =*((int*)p);int y =*((int*)p +1);printf("父进程[%d]读取数据:x=%#x y=%#x\n",getpid(), x, y);// 3.4 解除映射if(munmap(p, SHM_SIZE)==-1){perror("munmap failed");exit(3);}printf("父进程[%d]解除映射\n",getpid());// 3.5 销毁共享内存if(shm_unlink("/shmPosix")==-1){perror("shm_unlink");exit(4);}printf("父进程[%d]销毁共享内存, 结束进程\n",getpid());return0;}else{printf("Error in fork\n");exit(1);}return0;}

编译:gcc shm_posix.c -lrt

运行结果:
在这里插入图片描述


在这里插入图片描述

🎄四、信号

信号也可以勉强算作进程间通信的一种方式。信号是一种事件通知机制,当接收到该信号的进程会执行相应的操作。

进程可以通过kill函数给另一进程发送信号,收到信号的进程,对信号的处理有三种方式:忽略、捕捉和默认动作。

关于进程间的信号的,在前面的文章有介绍了,这里不再赘述。可以参看文章:
进程间通信 | 信号 (带C语言例子,8352字详细讲解)


在这里插入图片描述

🎄五、套接字

前面提到的通信方式都是在同一台主机上进行进程间通信,如果想要在不同主机上的进程进行通信,则需要用到socket(套接字)。

Socket可以在不同主机之间的进程进行通信,当然也可以在同一主机的不同进程进行通信。

Socket是操作系统提供给程序员操作网络的接口,根据底层不同的实现方式,通信方式也不同。

关于socket的内容,在后面的文章会介绍,这里先提一下,有个了解,知道它也是进程间通信的重要方式之一就行了。


在这里插入图片描述

🎄六、总结

本文介绍进程间通信的五种方式:POSIX消息队列、POSIX信号量、POSIX共享内存、信号、套接字。
想了解另外5种方式的,可以看上篇文章:
Linux 进程间通信的10种方式(1)

在这里插入图片描述
如果文章有帮助的话,点赞👍、收藏⭐,支持一波,谢谢 😁😁😁


本文转载自: https://blog.csdn.net/wkd_007/article/details/135758984
版权归原作者 wkd_007 所有, 如有侵权,请联系我们删除。

“【Linux C | 进程】Linux 进程间通信的10种方式(2)”的评论:

还没有评论