一.IPC背景
进程之间需要同步处理,同步需要通信,普通文件就是最基本的通信手段。普通文件IPC有一个进程改变另一个进程无法感知这种改变的问题。
解决办法:用特殊的文件:管道文件。
二.管道文件
1.创建管道文件
mkfifo()或使用linux命令mkfifo
2.管道文件的特点
read管道文件时,没有数据会阻塞,而且read后数据被删除
数据有序,先进先出
打开的描述符可以读写(two-way双工)shutdown可以关掉读写
管道文件的数据关闭后,数据不能持久,关闭后数据丢失。
管道的数据存储在内核的缓冲中
三,匿名管道
发现有名的管道的名字仅仅是内核是否返回同一个文件描述符的标识而已
所以当管道名失去标识作用的时候,实际可以不要名字。
在父子进程之间:打开文件描述符后创建进程
父子进程都有描述符号。管道文件没有价值
所以在父子进程中,引入一个没有名字的管道:匿名管道。
结论:匿名管道只能使用在父子进程之间。
1.创建匿名管道
int pipe(int fd[2])//创建管道,打开管道,拷贝管道,对二个fd这个关闭读,一个关闭写
fd[0]:只读
fd[1]:只写
注意:数据无边界
2.使用匿名管道
二.基于内存的通信
一组内核内存的工具 ipcs(-m -q-s)ipcrm
1.普通的父子进程之间的匿名内存内享映射
2.内核共享内存
编程模型
2.1创建共享内存,得到一个ID shmget
int shmget(key_tkey,//约定创建与访问的是同一个共享内存(ID唯一),可将目录(唯一)转化成整数,ftok()产生唯一的key_t
int size,共享内存大小
int flags)//共享内存的属性与权限 创建(IPC_CREAT)、IPC_EXCL防止覆盖
返回:成功返回ID,失败:返回-1
2.2把ID映射成虚拟地址(挂载) shmat
void *shmat(intshmid,
const void *shmaddr,首地址,0表示系统指定首地址
int flags)//挂载方式,建议为零
返回(int *)-1挂载失败,成功返回虚拟地址2.3使用虚拟地址访问内核共享内存 使用任何内存函数、运算符
str*** mem*** += ++ [] ->
2.4卸载虚拟地址 shmdt
2.5删除共享内存 shctl(修改/获取共享内存的属性)
int shmctl(int id,//被操作的共享内存ID
int how,//操作方式
struct shmid_ds *buf)//共享内存属性
3.内核共享队列(有序)
编程模型:
3.1创建共享队列/得到消息队列msgget
3.2使用消息队列(发送msgsnd/接收消息msgrcv)
3.3删除队列msgctl
创建消息队列
int msgget(key_t,int)
发送消息
int msgsnd(
int id,//消息队列ID
const void *msg,//要发送的消息
size_t len,//消息的长度
int flags//发送消息的方式,建议为0
);
返回:-1失败0成功
第二个参数的消息有固定的格式
4字节:表示消息的类型
若干字节:消息内容
第三个参数:
消息的大小,不包含类型的4个字节
四.信号量
1.信号的不足:必须知道通信对方进程,信号是一对一的形式,缺乏多对多的支持
2.信号量(semaphore)信号灯/信号量
信号量是共享内存整数数组,根据需要定指定的数据长度
信号量就是根据数组中的值,决定阻塞还是解除阻塞
3.编程
3.1创建或者得到信号量 semget
int semget(key_tkey,
int nums, //信号量数组个数
int flags);//控制信号量的创建标记创建为IPC_CREAT|IP_EXCL |0666 打开信号量为0
返回: -1:失败
>=:成功返回信号量的ID
3.2初始化信号量中指定下标的值 semctl
3.3根据信号量阻塞或者解除阻塞 semop
int semop(
int semid,//信号量ID
struct sembuf*op,//对信号量的操作
size_t nums。//第二个参数的个数
);
返回值:-1:时失败
3.4删除信号量 semctl
int semctl(int semid,
int nums,//对IPC_RMID无意义
int cmd,//SETVALIPC_RMID
...);//对IPC_RMID无意义
struct sembuf
{
int sem_num;//下标
int sem_op;
int sem_flg;//建议为0
}
sem_op:
前提条件:信号量是unsignedshort int,不能小于0
-:够减,则semop马上返回,不够减,则阻塞
+:执行+操作
0:判定信号量大于0,则阻塞,直到为0