标签:小结 post 内存区域 bsp semi emc 文件描述符 exec函数 mdt
进程间通信(IPC,InterProcess Communication)是指在不同进程之间传播或交换信息。
IPC的方式通常有管道(包括无名管道和命名管道)、消息队列、信号量、共享存储、Socket、Streams等。其中 Socket和Streams支持不同主机上的两个进程IPC。
一、一些基本概念
进程间通信(IPC):进程之间交换数据的过程叫进程间通信。
进程间通信的方式:
简单的进程间通信:
命令行:父进程通过exec函数创建子进程时可以附加一些数据。
环境变量:父进程通过exec函数创建子进程顺便传递一张环境变量表。
信号:父子进程之间可以根据进程号相互发送信号,进程简单通信。
文件:一个进程向文件中写入数据,另一个进程从文件中读取出来。
命令行、环境变量只能单身传递,信号太过于简单,文件通信不能实时。
XSI通信方式:X/open 计算机制造商组织。
共享内存、消息队列、信号量
网络进程间通信方式:网络通信就是不同机器的进程间通信方式。
传统的进程间通信方式:管道
二、管道
1、管道是一种古老的通信的方式(基本上不再使用)
2、早期的管道是一种半双工,现在大多数是全双工。
3、有名管道(这种管道是以文件方式存在的)。
int mkfifo(const char *pathname, mode_t mode);
例子:
管道通信的编程模式:
进程A 进程B
创建管道mkfifo
打开管道open 打开管道
写/读数据read/write 读/写数据
关闭管道close 关闭管道
4、无名管道:由内核帮助创建,只返回管道的文件描述符,看不到管道文件,但这种管道只能用在fork创建的父子进程之间。
int pipe(int pipefd[2]);
pipefd[0] 用来读数据
pipefd[1] 用来写数据
以Linux中的C语言编程为例。
此程序是一个简单的通过无名管道实现进程间的通信的程序!
三、共享内存
1、由内存维护一个共享的内存区域,其它进程把自己的虚拟地址映射到这块内存,然后多个进程之间就可以共享这块内存了。
2、这种进程间通信的好处是不需要信息复制,是进程间通信最快的一种方式。
3、但这种通信方式会面临同步的问题,需要与其它通信方式配合,最合适的就是信号。
共享内存的编程模式:
1、进程之间要约定一个键值
进程A 进程B
创建共享内存
加载共享内存 加载共享内存
卸载共享内存 卸载共享内存
销毁共享内存
int shmget(key_t key, size_t size, int shmflg);
功能:创建共享内存
size:共享的大小,尽量是4096的位数
shmflg:IPC_CREAT|IPC_EXCL
返回值:IPC对象标识符(类似文件描述符)
void *shmat(int shmid, const void *shmaddr, int shmflg);
功能:加载共享内存(进程的虚拟地址与共享的内存映射)
shmid:shmget的返回值
shmaddr:进程提供的虚拟地址,如果为NULL,操作系统会自动选择一块地址映射。
shmflg:
SHM_RDONLY:限制内存的权限为只读
SHM_REMAP:映射已经存的共享内存。
SHM_RND:当shmaddr为空时自动分配
SHMLBA:shmaddr的值不能为空,否则出错
返回值:映射后的虚拟内存地址
int shmdt(const void *shmaddr);
功能:卸载共享内存(进程的虚拟地址与共享的内存取消映射关系)
int shmctl(int shmid, int cmd, struct shmid_ds *buf);
功能:控制/销毁共享内存
cmd:
IPC_STAT:获取共享内存的属性
IPC_SET:设置共享内存的属性
IPC_RMID:删除共享内存
buf:
记录共享内存属性的对象
例子:
程序A
程序B:
上面两个程序分别运行得到进程A和进程B
通过获取进程id用kill函数来发送信号,从而实现A和B的通信,两个程序通过共享内存通信
四、消息队列
1、消息队列是一个由系统内核负责存储和管理、并通过IPC对象标识符获取的数据链表。
int msgget(key_t key, int msgflg);
功能:创建或获取消息队列
msgflg:
创建:IPC_CREAT|IPC_EXEC
获取:0
int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
功能:向消息队列发送消息
msqid:msgget的返回人值
msgp:消息(消息类型+消息内容)的首地址
msgsz:消息内存的长度(不包括消息类型)
msgflg:
MSG_NOERROR:当消息的实际长比msgsz还要长的话,
则按照msgsz长度截取再发送,否则产生错误。
ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp,int msgflg);
功能:从消息队列接收消息
msgp:存储消息的缓冲区
msgsz:要接收的消息长度
msgtyp:消息的的类型(它包含消息的前4个字节)
msgflg:
MSG_NOWAIT:如果要接收的消息不存在,直接返回。
否则阻塞等待。
MSG_EXCEPT:从消息队列中接收第一个不msgtyp类型的第一个消息。
int msgctl(int msqid, int cmd, struct msqid_ds *buf);
功能:控制/销毁消息队列
cmd:
IPC_STAT:获取消息队的属性
IPC_SET:设置消息队列的属性
IPC_RMID:删除消息队列
例子:
A程序
B程序
进程A和B通过消息队列完成IPC
五、信号量
信号量(semaphore)与已经介绍过的 IPC 结构不同,它是一个计数器。信号量用于实现进程间的互斥与同步,而不是用于存储进程间通信数据。
信号量用于进程间同步,若要在进程间传递数据需要结合共享内存。
信号量基于操作系统的 PV 操作,程序对信号量的操作都是原子操作。
每次对信号量的 PV 操作不仅限于对信号量值加 1 或减 1,而且可以加减任意正整数。
支持信号量组。
最简单的信号量是只能取 0 和 1 的变量,这也是信号量最常见的一种形式,叫做二值信号量(Binary Semaphore)。而可以取多个正整数的信号量被称为通用信号量。
Linux 下的信号量函数都是在通用的信号量数组上进行操作,而不是在一个单一的二值信号量上进行操作。
当semget
创建新的信号量集合时,必须指定集合中信号量的个数(即num_sems
),通常为1; 如果是引用一个现有的集合,则将num_sems
指定为 0 。
在semop
函数中,sembuf
结构的定义如下:
其中 sem_op 是一次操作中的信号量的改变量:
若sem_op > 0
,表示进程释放相应的资源数,将 sem_op 的值加到信号量的值上。如果有进程正在休眠等待此信号量,则换行它们。
若sem_op < 0
,请求 sem_op 的绝对值的资源。
sem_flg
有关。
IPC_NOWAIT
,则semop函数出错返回EAGAIN
。IPC_NOWAIT
,则将该信号量的semncnt值加1,然后进程挂起直到下述情况发生:若sem_op == 0
,进程阻塞直到信号量的相应值为0:
sem_flg
决定函数动作:
IPC_NOWAIT
,则出错返回EAGAIN
。IPC_NOWAIT
,则将该信号量的semncnt值加1,然后进程挂起直到下述情况发生:在semctl
函数中的命令有多种,这里就说两个常用的:
SETVAL
:用于初始化信号量为一个已知的值。所需要的值作为联合semun的val成员来传递。在信号量第一次使用之前需要设置信号量。IPC_RMID
:删除一个信号量集合。如果不删除信号量,它将继续在系统中存在,即使程序已经退出,它可能在你下次运行此程序时引发问题,而且信号量是一种有限的资源。上面的例子如果不加信号量,则父进程会先执行完毕。这里加了信号量让父进程等待子进程执行完以后再执行。
总结:进程间通信是实现两个程序传输数据的重要手段,非常值得学习和掌握
标签:小结 post 内存区域 bsp semi emc 文件描述符 exec函数 mdt
原文地址:https://www.cnblogs.com/dachao0426/p/9373947.html