信号灯
信号灯(semaphore)它是不同进程间或一个给定进程内部不同线程间同步的机制System V的信号灯是一个或者多个信号灯的一个集合(允许对集合中的多个计数信号灯进行同时操作)。其中的每一个都是单独的计数信号灯。而Posix信号灯指的是单个计数信号灯。
在linux中我们可以通过下面的指令来查看信号灯
ipcs -s
也可以通过下面的命令删除信号灯
ipcrm -s 【semid】
一、信号灯相关函数
1、semget函数
函数原型
int semget(key_t key, int nsems, int semflg);
所需头文件
#include <sys/ipc.h>
#include <sys/sem.h>
#include <sys/types.h>
返回值
成功:返回所创建的信号灯的文件id
失败:-1
参数
key标识信号灯的键值(就像文件的标识是文件名):0 / IPC_PRIVATE
nsems:信号灯集中包含的信号灯数目
emflg:信号灯集的访问权限
其中,当key的取值为IPC_PRIVATE,则函数semget()将创建一块新的信号灯;通过这种方式创建的信号灯,一般用来亲缘关系的进程间通信,非亲缘关系之间我们一般是通过ftok这个函数获取键值
(1)当key为ftok所获取的
ftok函数原型
key_t ftok(const char *pathname,int proj_id);
所需头文件
#include <sys/types.h>
#include <sys/ipc.h>
返回值
成功:返回key
失败:-1
参数
pathname就是你指定的文件名(已经存在的文件名),一般使用当前目录
proj_id是子序号,它是一个8bit的整数。即范围是0~255
(2)当key为IPC_PRIVATE时如
#include <stdio.h>
#include <unistd.h>
#include <sys/sem.h>
#include <sys/types.h>
#include <sys/ipc.h>
int main()
{
system("ipcs -s");
int semid = semget(IPC_PRIVATE,3,0777);
if(semid == -1)
{
printf("creat semaphore fail\n");
return -1;
}
else
{
printf("creat semphore success, semid = %d\n",semid);
}
system("ipcs -s");
return 0;
}
2、semctl(信号灯的控制(初始化/删除)
函数原型
int semctl ( int semid, int semnum, int cmd…/*union semun arg*/);
所需头文件
#include <sys/types.h>
#include <sys/sem.h>
#include <sys/ipc.h>
返回值
成功:0
失败:-1
参数
semid:信号灯集ID
semnum: 要操作的集合中的信号灯编号
cmd:
GETVAL:获取信号灯的值,返回值是获得值
SETVAL:设置信号灯的值,需要用到第四个参数联合体
IPC_RMID:从系统中删除信号灯集合
union semun arg:是一个联合体
如果只想进行删除信号灯操作那就将cmd随便设置一个信号灯的编号,并将这个参数省略不写 或者写为NULL
如果想要设置信号灯的值那就得自行定义联合体
联合体
联合体的val就是要对待操作的信号灯的某一个信号量初始化的值,所以在初始化信号灯中某一个信号量的值之前必须先定义这个联合体,并给val赋值
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) */
};
则初始化值的操作例如:
#include <stdio.h>
#include <unistd.h>
#include <sys/sem.h>
#include <sys/ipc.h>
#include <sys/types.h>
union semun
{
int val;
struct semid_ds *buf;
unsigned short *array;
struct seminfo *__buf;
};
int main()
{
system("ipcs -s");
int semid = semget(IPC_PRIVATE,3,0777);
if(semid == -1)
{
printf("creat semaphore fail\n");
return -1;
}
else
{
printf("creat semaphore success, semid = %d\n",semid);
}
union semun mysem[3];
mysem[0].val = 1;
mysem[1].val = 2;
mysem[2].val = 3;
semctl(semid,0,SETVAL,mysem[0]); // init first semaphore
semctl(semid,1,SETVAL,mysem[1]); // init second semaphore
semctl(semid,2,SETVAL,mysem[2]); // init third semaphore
semctl(semid,0,IPC_RMID,NULL);
system("ipcs -s");
return 0;
}
则删除信号灯的操作例如:
#include <stdio.h>
#include <unistd.h>
#include <sys/sem.h>
#include <sys/ipc.h>
#include <sys/types.h>
int main()
{
system("ipcs -s");
int semid = semget(IPC_PRIVATE,3,0777);
if(semid == -1)
{
printf("creat semaphore fail\n");
return -1;
}
else
{
printf("creat semaphore success, semid = %d\n",semid);
}
int ctlret = semctl(semid,1,IPC_RMID,NULL);
if(ctlret == -1)
{
printf("delete semaphore fail\n");
return -2;
}
else
{
printf("delete semaphore success, semid = %d\n",semid);
}
system("ipcs -s");
return 0;
}
3、semop(信号灯集的信号量进行P/V操作)
函数原型
int semop ( int semid, struct sembuf *opsptr, size_t nops);
所需头文件
#include <sys/types.h>
#include <sys/sem.h>
#include <sys/ipc.h>
返回值
成功:0
失败:-1
参数
semid:信号灯id
opsptr:结构体
对信号量的操作在结构体中
struct sembuf
{
unsigned short sem_num; //要操作的信号灯的编号
short sem_op; // 0 : 等待,直到信号灯的值变成0 // 1 : 释放资源,V操作 // -1 : 分配资源,P操作
short sem_flg; //0(阻塞),IPC_NOWAIT, SEM_UNDO
};
例如:semop(semid, &mysembuf, 1);
申请资源 P操作:
mysembuf.sem_num = 想要操作的信号量的编号;
mysembuf.sem_op = -1;
mysembuf.sem_flg = 0;
释放资源 V操作:
mysembuf.sem_num = 想要操作的信号量的编号;
mysembuf.sem_op = 1;
mysembuf.sem_flg = 0;
nops: 要操作的信号灯的个数
例如:
通过信号灯是的多线程进程中main先运行完后fun在运行
注意:在linux编译的时候要加 -lpthread
#include <stdio.h>
#include <unistd.h>
#include <sys/sem.h>
#include <sys/ipc.h>
#include <sys/types.h>
#include <pthread.h>
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) */
}semun;
int semid;
union semun mysem;
struct sembuf mybuf;
void *fun(void *arg)
{
int i;
mybuf.sem_op = -1;
semop(semid,&mybuf,1);
for(i=0;i<5;i++)
{
sleep(1);
printf("B\n");
}
}
int main()
{
int i;
semid = semget(IPC_PRIVATE,3,0777);
if(semid == -1)
{
printf("creat semaphore fail\n");
return -1;
}
else
{
printf("creat semaphore success, semid = %d\n",semid);
}
mysem.val = 0;
semctl(semid,1,SETVAL,mysem);
mybuf.sem_num = 1;
mybuf.sem_flg = 0;
pthread_t id;
int ret = pthread_create(&id,NULL,fun,NULL);
if(ret == 0)
{
printf("creat pthread success\n");
}
else
{
printf("creat pthread fail,ret = %d\n",ret);
}
for(i=0;i<5;i++)
{
sleep(1);
printf("A\n");
}
mybuf.sem_op = 1;
semop(semid,&mybuf,1);
while(1);
return 0;
}
二、进程间通讯
1、有亲缘关系通讯:
通过信号灯实现父子间程序的先后顺序
#include <stdio.h>
#include <unistd.h>
#include <sys/sem.h>
#include <sys/ipc.h>
#include <sys/types.h>
union semun
{
int val;
};
int main()
{
int semid = semget(IPC_PRIVATE,3,0777);
if(semid == -1)
{
printf("creat semaphore fail\n");
return -1;
}
else
{
printf("creat semaphore success, semid = %d\n",semid);
}
system("ipcs -s");
union semun mysem;
mysem.val = 0;
int ctlret = semctl(semid,1,SETVAL,mysem);
if(ctlret == -1)
{
printf("set semaphore val fail\n");
return -2;
}
else
{
printf("set semaphore val success\n");
}
struct sembuf mybuf;
mybuf.sem_num = 1;
mybuf.sem_flg = 0;
pid_t pid = fork();
if(pid < 0)
{
printf("fork fail\n");
return -3;
}
if(pid>0)
{
int i = 10;
while(i)
{
printf("A\n");
i--;
}
mybuf.sem_op = 1;
semop(semid,&mybuf,1);
while(1);
}
if(pid==0)
{
int i = 10;
mybuf.sem_op = -1;
semop(semid,&mybuf,1);
while(i)
{
printf("B\n");
i--;
}
}
return 0;
}