进程间通信

1、进程间通信概述
进程间通信(IPC)是指在两个或者多个不同的进程间传递或者交换信息,通过信息的传递建立几个进程间的联系,协调一个系统中的多个进程之间的行为。
1.1进程间通信的工作原理
进程与进程支架你是相互独立的,各自运行在自己的虚拟内存中,要想在进程与进程之间建立联系,需要通过内核,在内核中开辟一块缓冲区,两个进程的信息在缓冲区中进行交换或者传递
1.2进程间通信的主要分类

  • 管道通信
  • 共享内存通信
  • 信号量通信
  • 消息队列通信
  • 套接字(SOCKET)通信
  • 全双工管道通信

2、管道与命名管道
管道主要用于父子或者兄弟进程间的数据读写,命名管道则可以在无关联的进程间进行进行沟通传递数据
半双工:此时只能从管道的一端写数据,从另一端读取数据
2.1管道创建和管道关闭
pipe()函数用于在内核中创建一个管道,该管道一端用于读取管道中的数据,另一端用于将数据写入到管道中。
pipe()函数调用成功时返回值为0,否则返回-1。

#include <unistd.h>

int pipe(int filedes[2]);	//若成功则返回0,若出错则返回-1

//从管道中读数据:read()
//从管道中写数据:write()
//关闭管道的两端:close()

说明:参数filedes[2]是一个长度为2的文件描述符数组:

  • filedes[0]是读出端的文件描述符
  • filedes[1]是写入端的文件描述符
  • 当函数成功返回后,则自动维护了一个从filedes[1]到filedes[0]的数据通道
  • 注意:这个读出端和写出端是针对管道而言的,在后面进行通信时需要注意正确的选用文件描述符来打开与关闭相应的端口。(例如当需要将父进程中的某个数据写入管道,然后在子进程中将此数据读取出来时的连接方式应该是:父进程与管道读(出)端相连,然后子进程与管道的写(入)端相连)

2.2pipe()函数实现管道通信
实现过程:

  1. 在父进程中调用pipe()函数创建了一个管道,产生一个文件描述符filedes[0]指向管道的读端和另一个文件描述符filedes[1]指向管道的写端
  2. 在父进程中调用一个fork()函数创建一个一模一样的新进程,也就是所谓的子进程。父进程的文件描述符一个指向管道的读端,另一个指向管道的写端。同样,子进程也是如此。
  3. 在父进程中关闭指向管道写端的文件描述符filedes[1],在子进程中关闭指向管道读端的文件描述符filedes[0]。此时,就可以将父进程中的某个数据写入管道,然后在子进程中,将此数据读取出来。
    简而言之:先调用pipe()函数紧接着调用fork()函数,由于子进程自动继承父进程的数据段,则父子进程同时拥有管道的操作权,此时管道的方向取决于用户怎么维护该管道(即怎么开关父子进程的读端和写端)

2.3命名管道基本定义
命名管道可以解决没有关系的进程之间的通信问题。命名管道通常被称之为FIFO,管道和命名管道都遵循先进先出的原则。

命名管道与管道的区别:命名管道提供了一个路径名,该路径名以特殊的设备文件的形式存放在文件系统中。因此两个进程可以通过访问该路径来建立联系,进行两个进程间的数据交换。

2.3.1命名管道的创建
创建一个FIFO文件类似于创建一个普通文件,并且FIFO文件和其他文件一样,也是可以通过路径名来访问的。
(1)FIFO管道可以通过函数mkfifo创建,函数原型如下:

#include <sys/types.h>
#include <sys/stat.h>

int mkfifo(const char* pathname,mode_t mode);	//创建成功返回1,否则返回-1

第一个参数是一个普通的路径名,也即创建后FIFO文件的名字
第二个参数与打开普通文件的open函数中的mode参数相同,表示的是文件的权限

(2)命名管道也可以通过mknod来创建
注意:在向命名管道中进行读写操作前,需要先使用open()函数打开此文件。并且在命名管道中,如果只存在读数据或者写数据时,都会造成进程阻塞。

3、共享内存
多个进程共享同一块内存区域,在这一块内存区域上进行进程间的通信。
优点:更加快速、方便和高效
引入的问题:当多个进程同时读写一块共享内存时,内存中的数据就会产生会乱。因此需要特别注意同步机制。
对于每一个共享内存段,内核会为其维护一个shmid_ds类型的结构体
3.1共享内存相关操作
(1)shmget()函数
功能:创建或打开共享内存。在使用共享内存进行进程间通信前,通过shmget()函数创建一块共享的内存区域,如果已经存在一块共享内存区域,那么,该函数可以打开这个已经存在的共享内存。

#include<sys/ipc.h>
#include<sys/shm.h>

int shmget(key_t key,size_t size,int shmget);
调用成功时返回与参数key相关的共享内存区域的标识符,调用失败则返回-1。
参数说明:
key:共享内存的键值
size:新创建共享内存区域的大小,以字节表示
shmget:用于设置共享内存的访问权限,也表示调用函数的操作类型

(2)shmat()函数
功能:附加。将共享内存区域附加到指定进程的地址空间中。

#include<sys/types.h>
#include<sys/shm.h>

void *shamt(int shmid,const void*shmaddr,int shmflg);
调用成功时返回指向该共享内存区域的指针,调用失败则返回-1。
参数说明:
shmid:共享内存的标识符
shmaddr:指定进程的内存地址
shmflg:表示该进程的操作方式

(3)shmdt()函数
功能:分离。当某一进程不再使用该内存区域时,将调用该函数将附加的共享内存区域从该进程的地址空间中分离出来。

#include<sys/type.h>
#include<sys/shm.h>

int shmdt(const void*shmaddr)
调用成功时返回0,调用失败则返回-1
参数说明:
shamaddr:调用shamt()函数附加成功时返回的地址指针

(4)shmctl()函数
功能:实现对共享内存区域的多种控制操作

#include<sys/ipc.h>
#include<sys/shm.h>

int shmctl(int shmid,int cmd,struct shmid_ds *buf)
调用成功返回0,否则返回-1。
参数说明:
shmid:所要操作的共享内存段的标识符
cmd:指明了姚进行的操作
buf:指向shmid_ds结构体类型的指针

4、信号量
信号量是一种可以对多个进程访问共享资源进行有效控制的机制,它相当于一个整数计数器,统计了可供访问的共享资源的单元个数。
4.1信号量的工作原理
当有一个进程要求使用某一共享内存中的资源时,系统会首先判断该资源的信号量,也就是统计可以访问该资源的单元个数(比如考虑成打印机)。

  • 如果系统判断出该资源信号量值大于0,进程就可以使用该资源,并且信号量值要减1,当不再使用该资源并释放该资源时,信号量值再加1,方便其他进程使用时系统对其进行准确的判断。
  • 如果该资源的信号量值等于0,进程会进入休眠状态,等候其他正在使用该资源的进程结束后释放出该资源,然后进程会被唤醒,对该资源进行访问。
    一个信号量集有与其对应的结构体,该结构体中存储了信号量集的各种属性。
    4.2信号量的相关操作
    信号量并不能实现多个进程间的数据交换,只是起到了一个时间锁的功能。
    (1)semget()
    功能:创建信号量函数。创建一个新的信号量集操作和打开一个已经存在的信号量集的操作
#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/sem.h>

int semget(key_t key,int nsems,int semflg);
调用成功返回值为与参数key相关联的标识符,调用失败则返回-1
参数说明:
key:表示所创建的信号量集的键值
nsems:表示信号量集中信号量的个数。当semget()函数实现的功能是创建一个新的信号量集时,该参数才有效
semflg:用于设置信号量集的访问权限,也可以表示该函数的操作类型

(2)semop()
功能:信号量集操作函数。实现对信号量集中的信号量进行操作。

#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/sem.h>

int semop(int semid,struct sembuf*sops,unsigned nsops)
参数说明:
semid:表示要进行操作的信号量集的标识符
sops:为sembuf结构体指针变量,semop()函数通过此参数实现对单个信号量的具体操作
nsops:代表要操作的信号量

(3)semctl()
功能:信号量集的控制函数。例如使用前的初始化。

#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/sem.h>

int semctl(int semid,int semnum,int cmd, 或许要有其他参数 )
调用成功返回0,调用失败则返回-1
参数说明:
semid:表示要修改的信号量集的标识
semnum:标识需要修改的信号量集中的信号量个数
cmd:表示该函数的控制类型
该函数参数个数不确定,具体与cmd的控制类型取指有关,有可能存在第4个参数arg,用于读取或存储函数返回的结果。

5、消息队列
消息队列是一种通过链表结构组织的一组消息,消息是链表中具有一定格式及优先级的数据记录。多个进程通过消息队列的标识符对消息数据进行传送,实现进程间的通信。
每一个消息队列都有一个与之相对应的结构,用于定义一个消息队列的对象
5.1 消息队列的相关操作
(1)msgget()函数
功能:创建一个新的消息队列或打开一个已经存在的消息队列

#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/msg.h>

int msgget(key_t key,int msgflg);
调用成功时返回值为参数Key相关联的标识符,调用失败则返回-1
参数说明:
key:表示所创建的消息队列的键值
msgflg:用于设置消息队列的访问权限,也可以表示该函数的操作类型

(2)msgsnd()函数
功能:向消息队列中发送消息

#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/msg.h>

int msgsnd(int msqid,const *msgp,size_t msgsz,int msgflg);
调用成功时返回值为0,调用失败则返回-1
参数说明:
msqid:将消息发送到的消息队列的标识符
msgp:指向要发送的消息数据
msgsz:以字节数表示的消息数据的长度
msgflg:消息队列满时的处理方法,该参数可以是0值或IPC_NOWAIT

(3)msgrcv()函数
功能:接受消息队列中的消息数据

#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/msg.h>

ssize_t msgrcv(int msqid,void *msgq,size_t msgsz.long msgtyp,int msgflg);
函数调用成功返回值为0,调用失败则返回-1
参数说明:
msqid:从msqid标识符所代表的消息队列中接受消息
msgp:指向存放消息数据的内存空间
msgsz:以字节数表示的消息数据的长度
msgtyp:读取的消息数据的类型
msgflg:对读取的消息数据不满足要求时的处理

(4)msgctl()函数
功能:实现对消息队列的控制操作

#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/msg.h>

int msgctl(int msqid,int cmd,struct msqid_ds *buf);
参数说明:
msqid:消息队列的ID,也就是msgget()函数的返回值
cmd:控制内容取决于cmd的取指
buf:存放消息队列状态的结构体的地址
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值