一.基本概念
什么是进程间通信(IPC,Interprocess communication):是指两个或多个进程之间交换数据的过程叫进程间通信
进程之间为什么需要通信?
当需要多个进程协同工作高效率完成任务时,因为每个进程都是一个独立的个体(资源单位),进程之间就需要进行通信
进程间通信方式:
1.简单进程间通信:命令行参数,环境变量表,信号,文件
2.传统进程间通信:管道
3.XSI进程间通信:共享内存,消息队列,信号量
x-openx
si-信息接口
4.网络进程间通信:socket
二.传统的进程通信
管道是UNIX系统是最古老的进程间通信方式(基本不再使用),历史上的管道通常是半双工(只允许单向数据流动),现在的系统大都可以全双工,数据可以双向流动
1.有名管道(创建实体文件)
命令:mkfifo
函数: int mkfifo(const char *pathname, mode_t mode);
功能:创建管道文件
pathname:文件路径
mode:权限
返回值:成功返回0,失败返回-1
编程模型:
进程A 进程b
创建管道(mkfifo) ...
打开管道(open) 打开管道
读/写管道(read/write) 读/写数据
关闭管道(close) 关闭管道
删除管道(unlink) ...
2.无名管道(用于用于fork()创建的父子进程通信)
int pipe(int pipefd[2]);
功能:创建无名管道
pipefd:用来存储内核返回的文件描述符
pipefd[0] 用来读操作
pipefd[1] 用来写操作
返回值:成功返回0,失败返回-1
三.XSI进程间通信
X/open组织为UNIX系统设计的一套进程间通信机制,有共享内存,信息队列,信号量.
1.IPC标识
内核会为每个XSI的进程间通信维护一个IPC对象(XSI对象)
该对象通过一个非负整数来引用(类似于文件描述符).
与文件描述符不同的是,每用一个IPC对象标识符就持续+1,达到最大值时再从0开始
IPC标识需要程序员自己创建(类似于创建文件)
2.IPC键值
创建IPC键值的依据(类似创建文件的文件名),也是一个非负整数
1.自定义(不建议,可能会冲突)
2.自动生成(项目路径,项目编号)
key_t ftok(const char *pathname, int proj_id);
注意:项目路径一定要是有效路径,生成IPC键依靠的是路径而不是字符串
3.IPC对象的创建用到的宏
IPC_PRIVATE 创建IPC对象时永远创建成功
IPC_CREAT 对象存在则获取,不存在则创建
IPC_ECXL 如果对象已经存在,则创建失败
4.IPC对象销毁/控制用到的宏
IPC_STAT 获取IPC对象的属性
IPC_SET 设置IPC对象的属性
IPC_RMID 删除IPC对象
四.共享内存
在内核中开辟一块内存由IPC对象管理,进程A和进程B都用自己的虚拟内存与它映射,这样条目就共享了同一块内存,然后它们就可以通信了
特点:
1.不需要复制信息,是最快的一种进程间通信机制
2.需要考虑同步问题(必须借助其他的机制,如信号)
编程模型:
进程A 进程B
生成IPC键值 ftok 生成IPC键值 ftok
创建共享内存 shmget 获取共享内存 shmget
映射共享内存 shmat 映射共享内存 shmat
使用共享内存 *ptr 使用共享内存 *ptr
取消映射 shmdt 取消映射 shmdt
删除共享内存 shmctl ...
int shmget(key_t key, size_t size, int shmflg);
功能:创建/获取共享内存
key:IPC键,由ftok函数生成
size:共享内存的大小,最好是4096的整数倍,获取共享内存时,此值无效
shmflg:
0 获取共享内存
IPC_CREAT 创建
IPC_ECXL 如果存在,则创建失败
返回值:成功返回共享内存标识(IPC标识),失败返回-1
void *shmat(int shmid, const void *shmaddr, int shmflg);
功能:映射共享内存
shmid:共享内存标识符,shmget函数的返回值
shmaddr:进程提供的虚拟地址,与内核中的内存映射,也可以是NULL(,内核会自动选择一个地址映射)
shmflg:
0 自动选择一个地址映射
SHM_RDONLY 只读权限
SHM_RND 当shmaddr不为空时shmaddr向下取整页
返回值:映射成功后的虚拟地址
int shmdt(const void *shmaddr);
功能:取消虚拟内存与共享内存的映射
shmaddr:被映射过的虚拟地址
int shmctl(int shmid, int cmd, struct shmid_ds *buf);
功能:删除共享内存,获取/设置共享内存的属性
shmid:共享内存标识符,shmget的返回值
cmd:
IPC_STAT 获取共享内存的属性
IPC_SET 设置共享内存的属性
IPC_RMID 删除共享内存
struct shmid_ds {
struct ipc_perm shm_perm; /* Ownership and permissions */内存所有者及权限
size_t shm_segsz; /* Size of segment (bytes) */内存的大小
time_t shm_atime; /* Last attach time */最后的映射时间
time_t shm_dtime; /* Last detach time */最后的取消映射时间
time_t shm_ctime; /* Last change time */最后的修改时间
pid_t shm_cpid; /* PID of creator */创建者进程ID
pid_t shm_lpid; /* PID of last shmat(2)/shmdt(2) */最后映射/取消映射的进程ID
shmatt_t shm_nattch; /* No. of current attaches */映射的次数
...
};
struct ipc_perm {
key_t __key; /* Key supplied to shmget(2) */IPC键值
uid_t uid; /* Effective UID of owner */有效用户
gid_t gid; /* Effective GID of owner */有效用户组
uid_t cuid; /* Effective UID of creator */创建者的用户ID
gid_t cgid; /* Effective GID of creator */创建者组ID
unsigned short mode; /* Permissions + SHM_DEST and SHM_LOCKED flags */权限
unsigned short __seq; /* Sequence number */IPC标识
};
五.消息队列
信息队列就是由内核负责管理的一个管道,可以按顺序发送信息包(消息类型+消息内容),可以全双工工作
int msgget(key_t key, int msgflg);
功能:创建/获取消息队列
key:IPC键值,由ftok函数自动生成
msgflg:
0 获取消息队列
IPC_CREAT 创建消息队列
IPC_EXCL 如果存在则创建失败
返回值:消息队列标识
int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
功能:向消息队列发送消息
mspid:消息队列标识,msgget函数的返回值
msgp:结构指针
struct msgbuf {
long mtype; /* message type, must be > 0 */消息类型
char mtext[n]; /* message data */消息内容
};
msgsz:消息的长度,不包括消息类型sizeof(msgbuf)-sizeof(mtype)
msgflg:
0 阻塞,当消息队列满时,等待
IPC_NOWAIT 1 不阻塞,当消息队列满时,不等待
ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp,int msgflg);
功能:从信息队列按类型获取消息
mspid:消息队列标识,msgget函数的返回值
msgp:结构指针
struct msgbuf {
long mtype; /* message type, must be > 0 */消息类型
char mtext[n]; /* message data */消息内容
};
msgsz:要接收的消息的长度,可以长一点
msgtyp:要接收的消息类型
0 接收任意类型的消息(接收队列中第一个消息)
>0 只接收msgtyp类型的消息
<0 接收消息队列中小于等于msgtyp绝对值的消息,取小的那个
msgflg:
0 阻塞,消息队列中是否有对应类型的消息,没有则等待
1 不阻塞,消息队列中是否有对应类型的消息,没有则等待
MSG_NOERROR
1.消息类型正确,而消息的实际长度大于msgsz,则不接收消息并返回-1
2.如果msgflg带MSG_NOERROR标志,则把多余的消息截取成功接收
IPC_NOWAIT:如果消息队列没有要接收的消息,则不等待,返回-1
MSG_EXCEPT:接收消息队列中的第一个消息类型不是mstyp的消息,编译时添加-D_GNU_SOURCE参数
返回值:成功接收到的消息字节数
int msgctl(int msqid, int cmd, struct msqid_ds *buf);
功能:删除消息队列,或设置消息队列
mspid:消息队列标识,msgget函数的返回值
cmd:
IPC_STAT 获取消息队列的属性
IPC_SET 设置消息队列的属性
IPC_RMID 删除消息队列
struct msqid_ds {
struct ipc_perm msg_perm; /* Ownership and permissions */权限
time_t msg_stime; /* Time of last msgsnd(2) */最后一条消息的发送时间
time_t msg_rtime; /* Time of last msgrcv(2) */最后一次接收消息时间
time_t msg_ctime; /* Time of last change */最后一次修改时间
unsigned long __msg_cbytes; /* Current number of bytes inqueue (nonstandard) */消息队列中的字节数
msgqnum_t msg_qnum; /* Current number of messages in queue */消息队列中消息的个数
msglen_t msg_qbytes; /* Maximum number of bytes allowed in queue */消息队列中容纳的最大字节数
pid_t msg_lspid; /* PID of last msgsnd(2) */最后一次发送消息的进程
pid_t msg_lrpid; /* PID of last msgrcv(2) */最后一次接收消息的进程
};
返回值:成功返回0,失败返回-1
六.信号量
内核维护的计数量,用于管理多进程之间共享资源.
例如:有个变量n表示资源的数量,当有进程想要独占一个资源时,n的值要减1(可能减多个),如果n的值等于0(不够减),则进程阻塞,
直到n的值可以减再被唤醒,当资源使用完毕后n要加1(可能加多个)
#include
#include
#include
int semget(key_t key, int nsems, int semflg);
功能:创建/获取信号量
key:IPC键值
nsems:信号量的数量
semflg:
0 获取信号量
IPC_CREAT 创建信号量(以存在则获取,不存在则创建)
IPC_EXCL 如果已存在则创建失败
返回值:信号量的标识
int semop(int semid, struct sembuf *sops, unsigned nsops);
功能:操作信号量(对信号量进行加/减操作)
semid:信号量的标识,semget的返回值
sops:结构体数组
nsops:数组长度
struct sembuf{
unsigned short sem_num; /* semaphore number */ 信号量的下标
short sem_op; /* semaphore operation */ 操作
short sem_flg; /* operation flags */ 标记
}
sem_flg:
IPC_NOWAIT 当信号量不够减的时候不阻塞
SEM_UNDO 当进程结束时,信号量的值自动归还
int semtimedop(int semid, struct sembuf *sops, unsigned nsops, struct timespec *timeout);
功能:带时间限制的操作信号量
struct timespec{
time_t tv_sec ;秒
long tv_nsec ;纳秒
};
int semctl(int semid, int semnum, int cmd, ...);
功能:删除信号量,获取,设置信号量的属性,初始化信号量
cmd:
IPC_STAT 获取信号量的属性
IPC_SET 获取信号量的属性
IPC_RMID 删除信号量
struct ipc_perm {
key_t __key; /* Key supplied to semget(2) */
uid_t uid; /* Effective UID of owner */
gid_t gid; /* Effective GID of owner */
uid_t cuid; /* Effective UID of creator */
gid_t cgid; /* Effective GID of creator */
unsigned short mode; /* Permissions */
unsigned short __seq; /* Sequence number */
};
struct seminfo{
}
IPC_INFO 获取信号量的信息
SEM_INFO 设置信号量的信息
GETALL 获取所有信号量的值
GETNCNT 获取信号量的数量
GETVAL 获取某个信号量的值
SETALL 设置所有信号量的值
SETVAL 设置某个信号量的值
union semun {
int val; /* Value for SETVAL */
struct semid_ds *buf; /* Buffer for IPC_STAT, IPC_SET */
unsigned short *array; /* Array for GETALL, SETALL */
struct seminfo *__buf; /* Buffer for IPC_INFO
(Linux-specific) */
};
编程模型:
进程A 进程B
创建信号量 semget 获取信号量
初始化信号量 semctl ......
加/减信号量 semop 加减信号量
删除信号量 semctl ......
注意:信号量是用来计数的,一定要与资源对应
七.IPC命令
显示IPC对象
ipcs -m
ipcs -q
ipcs -s
ipcs -a
删除IPC对象
ipcrm -m ID
ipcrm -q ID
ipcrm -s ID