进程间的通信——管 道https://blog.csdn.net/q496958148/article/etails/79948367
进程间的通信——消息队列https://blog.csdn.net/q496958148/article/details/79951727
进程间的通信——共享内存https://blog.csdn.net/q496958148/article/details/79953349
说道信号量,则必须要提到俩个名词:“同步”与”互斥”
进程同步:
同步:就是多个进程相互协作共同完成一个任务。
例如:
- 一个理发店有一个理发师,一把剪子,和多个座位。
- 如果没有顾客,那么理发师就会休息
- 只要有一个顾客来了,就必须叫醒理发师
- 再有多余的顾客来了,那么他只能在座位上等待,如果没座位只能离开了。
进程互斥:
互斥:在同一时间某个资源只允许一个进程访问
例如:
- 有一个超市,入口处只有一个篮子
- 当第一个顾客进入时取一个篮子,而这时有第二个顾客来了,看到没篮子,只能坐在一旁等待第一个顾客买完归还篮子,第二个顾客取了篮子他才能进去购物。
而在上述例子中,这个超市在某一个时间段要么没有人,要么只能存在一个人。而这时超市这个资源被称为临界资源或者互斥资源。
信号量与P 、V:
信号量:
互斥:P V 在同一个进程内
同步:P V 在不同进程中
信号量值的含义:
S>0:S代表可用的资源
S=0:表示无可用资源,无等待队列
S<0:S的数值表示等待队列的进程个数
P、V操作:
S为一个信号量(互斥模型中S通常设置为1,同步模型中S通常设置为0)
P原语操作步骤:
(1)S=S-1;
(2)若S>=0,则该进程继续运行;
(3)若S<0.则该进程被阻塞,并将它插入该信号量的等待队列中。
V原语操作步骤:
(1)S=S+1;
(2)若S>0,则该进程继续运行;
(3)若S<=0.则从信号量的等待队列中唤醒第一个进程,
使其变为就绪状态,然后再返回原进程继续执行
互斥模型:(S通常为1)
p1进程 p2进程
P(S); P(S);
F(); F();
V(S); V(S);
分析如下:
- 首先p1进程先执行P操作这候S=S-1=0;
- 然后执行F();这时候p2进程来了,先进行P操作S=S-1=0-1=-1;这时候 p2进程被阻塞,进入等待队列。
- 等到p1进程执行完F();进行V操作S=S+1=-1+1=0;这时候唤醒消息队列的第一个位置。
- p2进程被唤醒,执行F();之后如果再来个p3进程,类似上述做法。
同步模型:(S通常为0)
p1进程 p2进程
P(S); V(S);
分析如下:
- 首先进程p1先进行P操作S=S-1=-1;这时候p1进程进入等待队列
- 这时候p2进程来了,进行V操作S=S+1=-1+1=0;唤醒等待队列的第一个进程p1
- 就这样不断进行同步操作。
信号量结构体:
struct semid_ds {
struct ipc_perm sem_perm; /* Ownership and permissions */
time_t sem_otime; /* Last semop time */
time_t sem_ctime; /* Last change time */
unsigned short sem_nsems; /* No. of semaphores in set */
};
信号量有关的函数:
- 创建和访问一个信号量集
int semget(key_t key, int nsems, int semflg);
//key 还是和之前的一样 “暗号”
//nsems 信号集中信号量的个数
//semflg IPC_CREAT:创建新的消息队列。 IPC_EXCL:与IPC_CREAT一同使用,表示如果要创建的消息队列已经存在,则返回错误。 IPC_NOWAIT:读写消息队列要求无法满足时,不阻塞。
//返回值成功返回信号量集的标识码,失败返回-1
- 控制信号量集
int semctl(int semid, int semnum, int cmd, ...);
//semid 由emget得到的标识码
//semnum 信号集的信号量的序号
//cmd 将要采取的动作,SETVAL设置信号量集中信号量的计数值,GETVAL获取信号量集中的信号量计数值,IPC_STAT把msqid_ds结构的数据设置为信号量集的当前关联值, IPC_SET在权限允许的情况下,把信号量集的当前关联值设置为msqid_ds数据结构中给出的值,IPC_RMID删除信号量集
//最后参数,根据命令不同而不同
//返回值成功为0,失败为-1
- 操作一个或者一组信号
int semop(int semid, struct sembuf *sops, unsigned nsops);
//semid 信号量集标识符
//sops 指向结构体数值的指针
//nsops 信号量的个数
//返回值成功为0,失败为-1
- sembuf结构体
struct sembuf{
short sem_num; /* semaphore number */
short sem_op; /* semaphore operation */
short sem_flg; /* operation flags */
};
//sem_num是信号量的编号
//sem_op是信号量进行一次PV操作减的数值,一般为-1,+1分别对应P操作和V操作
//sem_flg的俩个值是IPC_NOWAIT和IPC_UNDO
代码实例:
sem.h:
#pragma once
#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#define TAPH "."
#define NUM 0x6666
union semun {
int val;
struct semid_ds *buf;
unsigned short *array;
struct seminfo *_buf;
};
int createSem(int nums);
int getSem(int nums);
int initSem(int semid,int nums,int initval);
int P(int semid,int who);
int V(int semid,int who);
int destroySem(int semid);
sem.c:
#include "sem.h"
int createSem(int nums)
{
key_t key = ftok(TAPH,NUM);
if(key < 0)
{
perror("ftok");
return -1;
}
int semid = semget(key,nums,IPC_CREAT|IPC_EXCL|0666);
if(semid < 0)
{
perror("semget");
return -1;
}
return semid;
}
int getSem(int nums)
{
key_t key = ftok(TAPH,NUM);
if(key < 0)
{
perror("ftok");
return -1;
}
int semid = semget(key,nums,IPC_CREAT);
if(semid < 0)
{
perror("semget");
return -1;
}
return semid;
}
int initSem(int semid,int nums,int initval)
{
union semun _un;
_un.val = initval;
if(semctl(semid,nums,SETVAL,_un) < 0)
{
perror("semctl");
return -1;
}
return 0;
}
static int commPV(int semid,int who,int op)
{
struct sembuf buf;
buf.sem_num = who;
buf.sem_op = op;
buf.sem_flg = 0;
if(semop(semid,&buf,1) < 0)
{
perror("semop");
return -1;
}
return 0;
}
int P(int semid,int who)
{
return commPV(semid,who,-1);
}
int V(int semid,int who)
{
return commPV(semid,who,1);
}
int destroySem(int semid)
{
if(semctl(semid,0,IPC_RMID) < 0)
{
perror("semctl");
return -1;
}
return 0;
}
test.c:
#include "sem.h"
int main()
{
int semid = createSem(1);
initSem(semid,0,1);
pid_t id = fork();
if(id == 0)
{
int semid1 = getSem(0);
while(1)
{
P(semid1,0);
printf("A");
fflush(stdout);
usleep(123456);
printf("A ");
fflush(stdout);
usleep(312456);
V(semid1,0);
}
}
else
{
while(1)
{
P(semid,0);
printf("B");
fflush(stdout);
usleep(234561);
printf("B ");
fflush(stdout);
usleep(121134);
V(semid,0);
}
wait(NULL);
}
destroySem(semid);
return 0;
}
Makefile:
test:test.c sem.c
gcc $^ -o $@;
.PHONY:clean
clean:
rm -f test
没有PV操作前:
进行PV操作后:
PV操作规范了进程的执行,有了先后之分,相当与制定了规则,让所有的所需要对这块内存进行操作的进程有序且正常运行。
如果出现一下情况:
解决方法如下: