进程间通信:管道 ,信号量,共享内存,消息队列,套接字。(信号量,共享内存,消息队列)
同步机制:管道 消息队列。管道写端写满阻塞,管道为空,读端阻塞。
同步:同一时刻只能一个进程访问资源,若资源被占用程序就会被则色,对程序的控制同步。
管道
基础知识
管道:用来在两个进程之间传递数据,例如:ps -ef | grep “bash”,其中‘|’就是管道,作用是将ps命令的结果写入管道文件,然后grep再从管道文件中读出来该数据进行过滤。
管道:只能以只读只写管道。目的:进程间通讯创建好内存中分配
内存:断点不保存任何数据。
管道通讯特点:
1、半双工
2、管道没有数据,read阻塞
3、管道写端关闭,read返回0
4、管道读端关闭,写端产生异常(发送SIGPIPE,程序收到信号 异常终止)。
有名管道
可以在两个任意两个进程间通信。
管道文件的创建:mkfifo 命令(mkfifo FIFO)
a.c:向管道文件中写入数据
b.c:从管道文件中读取数据
a.c:
b.c:
我们发现a和b文件不懂再同一个终端运行,需要再两个不同终端进行操作,管道没有数据,read阻塞。
可以在任意两个进程间通信
无名管道
无名管道主要应用于父进程间的通信。
无名管道的创建:pipe(),成功返回0,失败返回-1.
int pipe(int fds[2]);fds[0]是管道读端的描述符,fds[1]是管道写端的描述符。主要在父子进程间通信使用。
举例:
文件描述符复制
dup 重定向,
int fd=open("a.txt",O_WRONLY|O_CREAT.0600);
dup(fd);
将fd复制一份,分配给文件表中未被使用的最小的一项。
0 | stdin |
---|---|
1 | stdout |
2 | stderr |
3 | a.txt |
信号量
基础知识
多个进程访问相同资源引起的冲突。
同步进程:同一时刻,只能有一个进程访问资源。
有一个特殊的变量值为可用资源的数目,其中有两个操作p操作和v操作,p操作获取资源-1,v操作获取资源+1.信号值为0,可用资源为0,p操作会被阻塞,v操作不会被阻塞。
我们可以举例理解: 有试衣间三个,数字3便是那个特殊的变量,几个人去抢占试衣间,人就相当于进程,当试衣间被占用就会进行p操作-1,当可用试衣间为0就不能进行p操作,用完试衣间让出来就会进行v操作+1.
临界资源:同一时刻只云相册一个进程访问的资源。
临界区:访问临界资源的代码段。
信号量API
semget() :创建或获取已存在信号量。
semget()成功返回信号量的ID,失败返回-1.
库函数:int semget(key_t key,int nsems,int semflag);
key:两个进程使用相同的key值,就可以使用同一个信号量。
nsem:内核维护的是一个信号量集,在新建信号量时,其指定信号量机中信号量的个数。
semflag可选:IPC_CREAT IPC_EXCL
semop() P,V操作,IPC_UNDO
p() 获取资源,程序异常终止,未释放资源,内核释放
IPC_NOWWAIT 打印错误信息。
semop()成功返回0,失败返回-1.
库函数:int semop(int semid,struct sembuf* sops,unsigned nsops);
struct sembuf{
unsigned short sem_num;//指定信号量集中的信号量下标
short sem_op;//其值为-1,代表p操作,其值为-1代表V操作
short sem_flg;//SEM_UNDO
}
semctl() 对信号量初始化 ,删除
控制信号量,semctl()成功返回0,失败返回-1,cmd选项:SETCAL IPC_RMID
union semun{
int val;
strucr semid_ds* buf;
unsigned short* array;
struct seminfo* _buf;
}
库函数:int semctl(int semid,int semnum,int cmd,……);
SETVAL IPC_RMID
ipcs:可以查看消息队列,共享内存,信号量的使用,使用ipcrm可以进行删除操作。
代码举例
进程a和进程b模拟访问打印机,进程a输出第一个字符‘A’,表示开始使用打印机,输出第二个‘A’表示结束使用,b进程操作同a一样。(肯定不能打印出ABAB之类穿插的字母,因为一个打印机只能同时被一个进程访问)
a.c:
b.c:
sem.h:
sem.c:
运行结果:
共享内存
基础知识
共享内存原理:多个进程在物理内存上有内存空间是共享的,多个进程在各自的逻辑地址空间写入数据,或获取数据,使用同一个空间,不需要数据的拷贝。
基础函数
int shmget (key_t key,size_t size,int shmflg);用于创建或者获取共享内存
返回共享内存的ID,失败返回1。key:不同的进程使用相同的key值可以获取到同一个共享内存。size:创建共享内存时,指定要申请的共享内存空间大小。shmflg:IPC_CREAT IPC_EXCL
void* shmat(int shmid,const void* shmaddr,int shmflg);//将申请的共享内存的物理内存映射到当前进程的虚拟地址空间上。成功返回共享内存的首地址,失败返回NULL。shmaddr:一般给NULL,由系统自动选择映射的虚拟地址空间。shmflg:一般给0.可以给SHM_RDONLY 为只读模式,其他的为读写模式
int shmdt(const void* shmaddr);断开当前进程的shmaddr指向的共享内存映射。成功返回0,失败返回-1.
int shmctl(int shmid,int cmd,struct shmid_ds *buf);//控制共享内存。成功返回0,失败返回-1。cmd:IPC_RMID。
代码举例
a.c:写入 b.c:读取
a.c:
b.c:
sem.c:
sem.h:
共享内存本身没有同步控制,共享内存与信号量相结合和以实现同步控制。
消息队列
消息队列
类似于队列,将消息存入队列中,然后从队列中读取元素。
基础函数
int msgget(key_t key,int msqflg);//创建或者获取一个消息队列。成功返回消息队列ID,失败返回-1。meqflg:IPC_CREAT。
int msgsnd(int msqid,const void* msqp,size_t msqsz,int msqflg);//发送一条消息,消息结构体:
struct msgbuf {
long mtype;
char mtext[1];
};
msgsnd()成功返回0,失败返回-1。msqsz:指定mtext中有效数据的长度。msqflg:一般设置为0可以设置IPC_NOWAII
ssize_t msgrcv(int msqid,void* msgp,size_t msqsz,long msqtyp,int msqflg);//接收一条消息。成功返回mtext中接收到的数据长度,失败返回-1。msqtyp:指定接收的消息类型,类型可以为0 。msqflg:一般设置为0可以设置IPC_NOWAIT
int msgctl(int msqid,int cmd,struct msqid_ds* buf);控制消息队列。成功返回0,失败返回-1。cmd:IPC_RMID.
代码举例
a.c:写入数据 b.c:读取数据
a.c:
b.c: