(一)概念
1.临界资源:同一时刻,只允许一个或有限个进程或者线程访问的资源。
2.临界区:访问临界资源的代码段。
3.原子操作:不可分割或者中断的操作,操作一旦开始执行,就必须执行结束,中途不能被任何原因打断。
4.信号量类似于计数器,是一个特殊的变量,值可以改变,但只能取正整数值,并且对它的加1和减1操作是原子操作。如果信号量值为0,那么再进行减1操作时就会阻塞。信号量的初始值,代表资源数量。
作用:控制多个进程对临界资源的访问,使程序在同一个时刻,只有一个进程访问临界资源(进行进程间同步控制)。原理就是控制程序的执行速度。
进程间同步控制:
- 同步执行:两个或多个进程需要协同执行,进程A的执行需要进程B提供支持。
- 异步执行:进程A和进程B互不干扰,在B给A发送数据前,A不会等待数据到达。
(二)信号量的操作:
所需头文件:
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
1.创建:
(1)函数:
int semget(key_t key,int nsems,int semflg);//第一次是同创建信号量,之后使用就是获取信号量
(2)参数:
- key:进程间通信键值,通过调用ftok()函数得到的键值。
- nsems:创建的信号量的个数。如果只是访问而不创建则可以指定该参数为0,一旦创建了该信号量,就不能更改其信号量个数,只要不删除该信号量,重新调用该函数创建该键值的信号量,该函数只能返回以前创建的值,不会重新创建。
- semflg:标识函数的行为及信号量的权限,其取值如下:
- IPC_CREAT:创建信号量
- IPC_EXCL:检测信号量是否存在
- 位或权限位:信号量位或权限位后可以设置信号量的访问权限,格式和open函数的mode_t一样。
(3)返回值:
- 成功:信号量标识符
- 失败:返回-1
2.控制信号量集合、信号量
(1)函数:
int semctl(int semid,int semnum,int cmd,...)//对信号量集合及集合中的信号量进行操作
(2)参数:
- semid:信号量集合标识符
- semnum:集合中信号量的序号,指定对哪个信号量进行操作,只对几个特殊的cmd操作有意义。
- cmd:信号量控制类型。semctl()函数可能有3个参数,也可能有4个参数,参数的个数由cmd决定。当有4个参数时,第4个参数为联合体。
union semun
{
int val;//信号量的值
struct semid_ds* buf;//信号量集合信息
unsigned short* array;//信号量值的数组
struct seminfo* _buf;//信号量限制信息
};
- GETVAL:获取信号量的值。此时函数有3个参数,semctl()函数的返回值即为信号量的值。
- SETVAL:设置信号量的值。此时函数有4个参数。第4个参数为联合体中的val,其值为信号量的值。
- IPC_STAT:获取信号量集合的信息。此时函数有4个参数。第4个参数为联合体中的_buf。
- IPC_SET:设置信号量集合的信息。此时函数有4个参数。第4个参数为联合体中的_buf。
- IPC_RMID:删除信号量集。此时函数有3个参数,第2个参数semnum不起作用。
- GETALL:获取所有信号量的值。此时函数有4个参数,第2个参数semnum不起作用。第4个参数为联合体中的array,其值用来存放所有信号量值的数组的首地址。
- SETALL:设置所有信号量的值。和上面的一样。
- IPC_INFO:获取信号量集合的限制信息。此时函数有4个参数,第2个参数semnum不起作用。第4个参数为联合体中的_buf。
- GETPID:获取信号的进程号,即最后操作信号量的进程。此时函数有3个参数。semctl()函数的返回值即为信号的进程号。
- GETNCNT:获取等待信号的值递增的进程数。此时函数有3个参数。semctl()函数的返回值即为进程数。
- GETZCNT:获取等待信号的值递减的进程数。此时函数有3个参数。semctl()函数的返回值即为进程数。
(3)返回值:
- 成功:0
- 失败:-1
3.操作信号量:
(1)函数:
int semop(int semid,struct sembuf* sops,unsigned nsops);//操作信号量,主要进行信号量加减操作
(2)参数:
- semis:信号量集合标识符
- sops:操作信号量的结构体(struct sembuf)数组的首地址(结构体定义在sys/sem.h),此结构体中的数据表明了对信号量进行的操作。
struct sembuf
{
unsigned short sem_num;//信号量的序号
short sem_op;//信号量的操作值
short sem_flg;//信号量的操作标识
};
sem_num:信号量集合中信号量的序号
sem_op取值如下:
- sem_op>0:信号量的值在原来的基础上加上此值。
- sem_op<0:如果信号量的值小于semop的绝对值,则挂起操作进程。如果信号量的值大于等于semop的绝对值,则信号量的值在原来的基础上减去semop的绝对值。
- sem_op=0:对信号量的值进行是否为0 的测试。若为0则函数立即返回,若不为0则阻塞调用进程。
sem_flag取值如下:
-
IPC_NOWAIT:在对信号量的操作不能执行的情况下使函数立即返回。
-
SEM_UNDO:当进程推出后,该进程对信号量进行的操作将被撤销。
-
nsops:操作信号量的结构体数组元素的个数。
(3)返回值:
- 成功:0
- 失败:-1
实例1:
#include <sys/sem.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <stdio.h>
#include <stdlib.h>
int main()
{
key_t key;
key = ftok(".",2016);
if(key == -1)
{
perror("ftok error");
}
system("ipcs -s");
int semid;
semid = semget(key,1,IPC_CREAT|0666);
if(semid == -1)
{
perror("semget error");
}
system("ipcs -s");
semctl(semid,0,IPC_RMID);
system("ipcs -s");
return 0;
}
运行结果:
实例2:
#include <sys/sem.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <stdio.h>
#include <stdlib.h>
#define IPC_INFO 3
int main()
{
key_t key;
key = ftok(".",2016);
if(key == -1)
{
perror("ftok error");
}
system("ipcs -s");
int semid;
semid = semget(key,1,IPC_CREAT|0666);
if(semid == -1)
{
perror("semget error");
}
system("ipcs -s");
struct seminfo buf;
semctl(semid,0,IPC_INFO,&buf);
printf("buf.semmni = %d\n",buf.semmni);
printf("buf.semmns = %d\n",buf.semmns);
printf("buf.semmnu = %d\n",buf.semmnu);
printf("buf.semmsl = %d\n",buf.semmsl);
printf("buf.semopm = %d\n",buf.semopm);
printf("buf.semume = %d\n",buf.semume);
printf("buf.semusz = %d\n",buf.semusz);
printf("buf.semvmx = %d\n",buf.semvmx);
printf("buf.semaem = %d\n",buf.semaem);
semctl(semid,0,IPC_RMID);
system("ipcs -s");
return 0;
}
运行结果: