基本概念:
什么是进程间通信:是指两个或多个进程之间交互数据的过程,因为进程之间是相互独立,为了协同工作必须交互数据。
进程间通信的分类:
简单的进程间通信:信号、文件、环境变量、命令行参数。
传统的进程间通信:管道文件(有名管道、匿名管道)
XSI进程间通信:共享内存、消息队列、信号量。
网络进程间通信:套接字
传统进程间通-管道
管道是UNIX系统中最古老的进程间通信方式,古老就意味着所有系统都支持,早期的管道都是半双工,现在有些系统的管道是全双工(但要假定系统仅支持半双工)。
管道是一种特殊的文件,它的数据在文件中是流动的,读取之后就消失了,如果文件中没有数据读取时会阻塞。
有名管道:基于有文件名的管道文件的通信。
编程模型:
进程A 进程B
创建管道
打开管道 打开管道
写数据 读数据
关闭管道 关闭管道
删除管道
创建管道文件:
1、命令:mkfifo file
2、函数:
int mkfifo(const char *pathname, mode_t mode);
功能:创建管道文件
pathname:管道文件路径
mode:管道文件权限
3、标准库函数:
FILE *popen(const char *command, const char *type);
功能:打开或创建管道
command:可执行文件名
type:
r 文件指针链接到可执行文件的标准输出
w 文件指针链接到可执行文件的标准输入
注意:适合获读取命令的执行结果。
int pclose(FILE *stream);
功能:关闭管道文件专用函数,不能与fclose混用
匿名管道:
注意:该函数只适合通过fork创建父进程之间使用。
int pipe(int pipefd[2]);
功能:创建一个匿名管道文件,返回管道文件的读权限fd和写权限fd
pipefd:用于存储管道文件fd的数组
pipefd[0] 用于读 pipefd[1] 用于写
XSI进程间通信:
X/open公司制定用于进程通信的系统接口。
XSI进程间通信都需要借助系统内核,需要创建内核对象,内核对象会以整数形式返回给用户态,相当于文件描述符,也叫IPC标识符。
编程模型:
父进程 子进程
获取一对fd
创建进程 拷贝一对fd
关闭读 关闭写
写数据 读数据
关闭写 关闭读
文件的创建打开需要借助文件名,IPC内核对象创建则需要借助IPC键值(整数),必须要确保IPC错误独一无二。
key_t ftok(const char *pathname, int proj_id);
功能:计算出一个独一无二的IPC键值
pathname:项目路径,不是依靠字符串计算的,而是依靠路径的位置,如果提供假的路径,可能会生成同样的IPC键值。
proj_id:项目编号
返回值:计算出的IPC键值
共享内存:
基本特点:
两个或多个进程之间共享一块由内核负责维护的内存,该内存可以与进程的虚拟内存映射。
优点:不需要复制信息,是最快的一种IPC机制。
缺点:需要考虑同步访问问题,一般使用信号。
int shmget(key_t key, size_t size, int shmflg);
功能:创建/获取共享内存
key:由进程提供一个独一无二的IPC键值
size:共享内存的大小,获取内存时此参数无意义一般设置为0。
shmflg:
IPC_CREAT:创建共享内存
IPC_EXCL:共享内存已经时返回错误
mode_flags:创建共享内存时需要提供权限
返回值:IPC标识符,错误返回-1。
void *shmat(int shmid, const void *shmaddr, int shmflg);
功能:虚拟内存与共享内存映射
shmid:IPC标识符
shmaddr:想映射的虚拟内存地址,为NULL时系统自动操作。
shmflg:
SHM_RND:只有shmaddr参数不为NULL有效,表示对shmaddr参数向下取内存页的整数倍,作为映射地址。
SHM_RDONLY:以只读方式映射共享内存
返回值:与共享内存映射后的虚拟内存地址,失败返回0xFFFFFFFF。
int shmdt(const void *shmaddr);
功能:取消映射
shmaddr:映射过的虚拟内存地址。
int shmctl(int shmid, int cmd, struct shmid_ds *buf);
功能:删除/控制共享内存
shmid:IPC标识符
cmd:
IPC_STAT 获取共享内存属性,则buf输出型参数
IPC_SET 设置共享内存属性,则buf是输入型参数
IPC_RMID 删除共享内存
buf:
struct shmid_ds {
struct ipc_perm shm_perm; 所有者相关信息
size_t shm_segsz; 共享内存字节数
time_t shm_atime; 最后映射时间
time_t shm_dtime; 最后取消映射时间
time_t shm_ctime; 最后改变时间
pid_t shm_cpid; 创建者进程号
pid_t shm_lpid; 最后映射/取消映射的进程号
shmatt_t shm_nattch;当前映射次数
...
};
struct ipc_perm {
key_t __key; 创建共享内存的IPC键值
uid_t uid; 当前使用共享内存的用户ID
gid_t gid; 当前使用共享内存的用户组ID
uid_t cuid; 创建共享内存的用户ID
gid_t cgid; 创建共享内存的用户组ID
unsigned short mode; 共享内存的权限
unsigned short __seq; 共享内存序列号
};
编程模型:
进程A 进程B
创建共享内存 获取共享内存
映射共享内存 映射共享内存
写数据并通知其它进程 接收到通知后读取数据
接收到通知后读取数据 写数据并通知其它进程
取消映射 取消映射
删除共享内存
消息队列:
基本特点:是由内核负责维护管理的数据链表,通过消息类型收发数据。
编程模型:
进程A 进程B
创建消息队列 获取消息队列
发送消息 接收消息
接收消息 发送消息
删除消息队列
int msgget(key_t key, int msgflg);
功能:创建/获取消息队列
key:IPC键值
msgflg:
IPC_CREAT 创建消息队列
IPC_EXCL 如果消息队列已经存在则返回错误
mode_flags:创建消息队列时需要提供权限
返回值:成功返回IPC标识符,错误返回-1。
int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
功能:向消息队列发送数据
msqid:IPC标识符
msgp:要发送的消息的首地址
struct msgbuf {
long mtype; // 消息类型
char mtext[5]; // 数据
};
msgsz:数据的字节数,不包含消息类型。
msgflg:
阻塞一般写0。
IPC_NOWAIT 当消息队列满时,不等待立即返回。
ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp,
int msgflg);
功能:从消息队列中读取数据
msqid:IPC标识符
msgp:存储数据结构首地址
msgsz:结构体的字节数
msgtyp:消息类型
>0 读取消息类型等于msgtyp的消息。
=0 读取消息队列第一条消息。
<0 读取消息类型小于abs(msgtyp)的消息,如果有多个则读取最小的。
msgflg:
IPC_NOWAIT 消息类型不符合时不阻塞,立即返回。
MSG_EXCEPT 如果msgtyp>0,则读取第一个消息类型不是msgtyp的消息。
MSG_NOERROR 如果不包含些标志,消息实际长度>msgsz,则返回错误不读取数据,若包含此标志,则读取msgsz个字节。
返回值:成功读取到的字节数
int msgctl(int msqid, int cmd, struct msqid_ds *buf);
功能:删除/获取消息的属性
msqid:IPC标识符
cmd:
IPC_STAT 获取消息队列的属性
IPC_SET 设置消息队列的属性
IPC_RMID 删除消息队列
buf:
struct msqid_ds {
struct ipc_perm msg_perm; 属主信息
time_t msg_stime; 最后发送时间
time_t msg_rtime; 最后接收时间
time_t msg_ctime; 最后修改时间
unsigned long __msg_cbytes; 当前消息队列字节数
msgqnum_t msg_qnum; 当前消息的数量
msglen_t msg_qbytes; 一条消息的最大字节数
pid_t msg_lspid; 最后发送者PID
pid_t msg_lrpid; 最后接收者PID
};
信号量:
基本特点:由内核共享的一个"全局变量",用于记录共享资源的数量,限制进程对共享资源的访问。
1、若变量的值大于0,说明可以使用资源,需要将变量-1,然后再用。
2、若变量的值等于0,说明没有资源可以使用,此时进程进入休眠,直到变量大于0,进程被唤醒,执行步骤1。
3、当资源使用完毕,变量的值+1,正在休眠的进程可以被唤醒。
int semget(key_t key, int nsems, int semflg);
功能:创建/获取信号量
key:IPC键值
nsems:信号量的数量,一般写1即可。
semflg:
IPC_CREAT 创建
IPC_EXCL 排斥
mode_flags:权限
返回值:IPC标识符,错误返回-1。
int semop(int semid, struct sembuf *sops, unsigned nsops);
功能:对信号量进程加减操作
sops:
struct sembuf{
unsigned short sem_num; 信号量的数量
short sem_op;
1 信号是加1
-1 如果信号量的值不能 则阻塞
0 等待信号量的值为零
short sem_flg;
IPC_NOWAIT 不阻塞
SEM_UNDO 如果进程终止没有还原信号量,系统将自动还原。
}
nsops:表示sops指针指向多少个结构体
int semctl(int semid, int semnum, int cmd, union semun arg);
功能:删除/控制信号量
semid:IPC标识符
semnum:信号量的数量
cmd:
IPC_STAT 获取信号量的属性
IPC_SET 设置信号量的属性
IPC_RMID 删除信号量
GETALL 获取所有信号量的值
GETNCNT 等待减信号的进程数量
GETVAL 通过返回值获取某个信号量的值 semnum 就是下标
GETZCNT 等待信号为0的进程数量
SETALL 设置所有信号量的值
SETVAL 设置某个信号量的值
arg:
union semun {
int val; 用于设置信号量的值
struct semid_ds *buf; 信号量属性
unsigned short *array; 批量获取/设置信号量的值
};