一 信号量的基本概念
信号量:它是一个特殊变量,只允许对它进行等待和发送信号这两种操作。
假设有一个信号量变量sv
P(sv):用于等待,如果sv的值大于零,就给它减去1,如果它的值等于零,就挂起该进程的执行。
V(sv):用于发送信号,如果有其他进程因等待sv而被挂起,就让它恢复运行,如果没有进程因等待sv而被挂起,就给它加1
二 信号量的相关函数
#include <sys/sem.h>
//semctl函数的作用是用来直接控制信号量信息 int semctl( int sem_id, //由semget返回的信号量标识符
int sem_num, //是信号量编号,当需要用到成组的信号量时,就要用到这个参数,它一般取值为0,表示这是第一个也是唯一的信号量
int command, //是将要采取的动作
... //它将会是一个union semun结构
);
//semget函数的作用是创建一个新信号量或取得一个已有信号量的键。 int semget(key_t key, //key是整数值,不相关的进程可以通过它访问同一个信号量
int num_sems, //指定需要的信号量数目,几乎总是取值为1
int sem_flags //是一组标志,与open函数的标志非常相似
); //在成功时返回一个正数(非零)值,它就是其他信号量函数将用到的信号量标识符,如果失败,则返回-1
//semop函数用于改变信号量的值 int semop( int sem_id, //由semget返回的信号量标识符
struct sembuf *sem_ops, //指向一个 sembuf 结构数组的指针
size_t num_sem_ops //信号操作结构的数量,恒大于或等于1
);
semun包含于semun.h头文件中
union semun{
int
val;
//SETVAL所设置的信号量集中的一个信号量的值
struct
semid_ds *
buf;
//IPC_STAT,IPC_SET存储的数据
unsigned
short
*
array;
//GETALL, SETALL返回值的数组
}
semop函数中的sembuf结构体
struct
sembuf{
short
sem_num;
//信号量编号,除非需要使用一组信号量,否则它的取值一般为0
short
sem_op;
//是信号量在一次操作中需要改变的数值,通常只会用到两个值,一个是-1,也就是P操作,它等待信号量变为己用;一个是+1,也就是V操作,它发送信号表示信号量现在已可用
short
sem_flg;
//通常设置为SEM_UNDO,它将使得操作系统跟踪当前进程对这个信号量的修改情况
}
semctl中第三个参数command取值如下:
·IPC_STAT:读取一个信号量集的数据结构semid_ds,并将其存储在semun中的buf参数中。
·IPC_SET:设置信号量集的数据结构semid_ds中的元素ipc_perm,其值取自semun中的buf参数。
·IPC_RMID:将信号量集从内存中删除。
·GETALL:用于读取信号量集中的所有信号量的值。
·GETNCNT:返回正在等待资源的进程数目。
·GETPID:返回最后一个执行semop操作的进程的PID。
·GETVAL:返回信号量集中的一个单个的信号量的值。
·GETZCNT:返回这在等待完全空闲的资源的进程数目。
·SETALL:设置信号量集中的所有的信号量的值。
·SETVAL:设置信号量集中的一个单独的信号量的值。
三 例子
#include <unistd.h>
#include
<stdlib.h>
#include
<stdio.h>
#include
<sys/sem.h>
#include
"
semun.h
"
static
int
set_semvalue(
void
);
static
void
del_semvalue(
void
);
static
int
semaphore_p(
void
);
static
int
semaphore_v(
void
);
static
int
sem_id;
int
main(
int
argc,
char
*
argv[]){
int
i;
int
pause_time;
char
op_char=
'
0
'
; srand((unsigned
int
)getpid()); sem_id
=semget((key_t)
1234
,
1
,
0666
|
IPC_CREAT);
if
(argc>
1
){
if
(!
set_semvalue()){ fprintf(stderr,
"
Failed to initialize semaphore\n
"
); exit(EXIT_FAILURE); } op_char
=
'
X
'
; sleep(
2
); }
//
进入和离开临界区域10次,在每次循环的开始,首先调用semaphore函数,它在程序进入临界区域时设置信号量以等待进入
for
(i=
0
;i<
10
;i++
){
if
(!
semaphore_p()){ exit(EXIT_FAILURE); } printf(
"
%c
"
,op_char); fflush(stdout); pause_time
=rand()%
3
; sleep(pause_time); printf(
"
%c
"
,op_char); fflush(stdout);
//
临界区域之后,调用semaphore_v来将信号量设置为可用,然后等待一段随机时间,再进入下一次循环。在整个循环语句执行完毕后,调用del_semvalue函数来清理代码
if
(!
semaphore_v()){ exit(EXIT_FAILURE); } pause_time
=rand()%
2
; sleep(pause_time); } printf(
"
\n%d - finished\n
"
,getpid());
if
(argc>
1
){ sleep(
5
); del_semvalue(); } exit(EXIT_SUCCESS); }
//
函数set_semvalue通过将semct1调用的command参数设置为SETVAL来初始化信号量
static
int
set_semvalue(
void
){ union semun sem_union; sem_union.val
=
1
;
if
(semctl(sem_id,
0
,SETVAL,sem_union)==-
1
){
return
0
; }
return
1
; }
static
void
del_semvalue(
void
){ union semun sem_union;
if
(semctl(sem_id,
0
,IPC_RMID,sem_union)==-
1
){ fprintf(stderr,
"
Failed to delete semaphore
"
); } }
//
semaphore_p对信号量做减1操作(等待)
static
int
semaphore_p(
void
){
struct
sembuf sem_b; sem_b.sem_num
=
0
; sem_b.sem_op
=-
1
; sem_b.sem_flg
=
SEM_UNDO;
if
(semop(sem_id,&sem_b,
1
)==-
1
){ fprintf(stderr,
"
semaphore_p failed\n
"
);
return
0
; }
return
1
; }
//
semaphore_v将sembuf结构中的sem_op设置为1,释放操作
static
int
semaphore_v(
void
){
struct
sembuf sem_b; sem_b.sem_num
=
0
; sem_b.sem_op
=
1
; sem_b.sem_flg
=
SEM_UNDO;
if
(semop(sem_id,&sem_b,
1
)==-
1
){ fprintf(stderr,
"
semaphore_v failed\n
"
);
return
0
; }
return
1
; }