sem_wait() 减小(锁定)由sem指定的信号量的值.如果信号量的值比0大,那么进行减一的操作,函数立即返回.如果信号量当前为0值,那么调用就会一直阻塞直到或者是信号量变得可以进行减一的操作(例如,信号量的值比0大),或者是信号处理程序中断调用
sem_trywait() 和 sem_wait()是一样的,除了如果不能够对信号量立即进行减一,那么sem_trywait()就会返回一个错误(错误号是AGAIN)而不是锁定.sem_timedwait() 和 sem_wait()是一样的,除了如果减一操作不能立即执行的话,abs_timeout 指定了调用应该被阻塞的时间限制.abs_timeout 参数指向了一个结构体指定了由秒和纳秒组成的绝对的超时值:从1970-01-01 00:00:00 +0000纪元开始的UTC,结构体的定义如下:struct timespec {time_t tv_sec/* Seconds */long tv_nsec/* Nanoseconds [0 .. 999999999] */}如果超时值已经超过了调用规定的值,那么信号量不能被立即锁定,之后sem_timedwait() 为超时失败(error设置为ETIMEDOUT).
如果操作立即生效,那么sem_timedwait() 永远不会返回超时的错误,不管abs_timeout的值.更进一步的是,在这种情况下abs_timeout值的有效性都不会检查. EINTR The call was interrupted by a signal handlersee signal(7).//调用被信号处理中断
EINVAL sem is not a valid semaphore.//sem不是有效的信号量
The following additional error can occur for sem_trywait()://下面的错误是sem_trywait()可能发生的:
EAGAIN The operation could not be performed without blocking (i.e., thesemaphore currently has the value zero).//除了锁定无法进行别的操作(如信号量当前是0值).
The following additional errors can occur for sem_timedwait()://下面的错误是sem_timedwait()可能发生的:
EINVAL The value of abs_timeout.tv_nsecs is less than 0, or greater than orequal to 1000 million.//abs_timeout.tv_nsecs 的值比0小或者大于等于1000毫秒(译者注:纳秒的值不能比0小,不能比1秒大)
ETIMEDOUTThe call timed out before the semaphore could be locked.//在信号量锁定之前就超时了 对这些函数,信号处理程序总是会中断阻塞,不管是否使用了sigaction(2)的SA_RESTART标志位.
信号量是包含一个非负整数型的变量,并且带有两个原子操作wait和signal。Wait还可以被称为down、P或lock,signal还可以被称为up、V、unlock或post。在UNIX的API中(POSIX标准)用的是wait和post。
对于wait操作,如果信号量的非负整形变量S大于0,wait就将其减1,如果S等于0,wait就将调用线程阻塞;对于post操作,如果有线程在信号量上阻塞(此时S等于0),post就会解除对某个等待线程的阻塞,使其从wait中返回,如果没有线程阻塞在信号量上,post就将S加1.
由此可见,S可以被理解为一种资源的数量,信号量即是通过控制这种资源的分配来实现互斥和同步的。如果把S设为1,那么信号量即可使多线程并发运行。另外,信号量不仅允许使用者申请和释放资源,而且还允许使用者创造资源,这就赋予了信号量实现同步的功能。可见信号量的功能要比互斥量丰富许多。
POSIX信号量是一个sem_t类型的变量,但POSIX有两种信号量的实现机制: 无名信号量 和 命名信号量 。无名信号量只可以在共享内存的情况下,比如实现进程中各个线程之间的互斥和同步,因此无名信号量也被称作基于内存的信号量;命名信号量通常用于不共享内存的情况下,比如进程间通信。
同时,在创建信号量时,根据信号量取值的不同,POSIX信号量还可以分为:
下面是POSIX信号量函数接口:
信号量的函数都以sem_开头,线程中使用的基本信号函数有4个,他们都声明在头文件semaphore.h中,该头文件定义了用于信号量操作的sem_t类型:
【sem_init函数】:
该函数用于创建信号量,原型如下:
该函数初始化由sem指向的信号对象,设置它的共享选项,并给它一个初始的整数值。pshared控制信号量的类型,如果其值为0,就表示信号量是当前进程的局部信号量,否则信号量就可以在多个进程间共享,value为sem的初始值。
该函数调用成功返回0,失败返回-1。
【sem_destroy函数】:
该函数用于对用完的信号量进行清理,其原型如下:
成功返回0,失败返回-1。
【sem_wait函数】:
该函数用于以原子操作的方式将信号量的值减1。原子操作就是,如果两个线程企图同时给一个信号量加1或减1,它们之间不会互相干扰。其原型如下:
sem指向的对象是sem_init调用初始化的信号量。调用成功返回0,失败返回-1。
sem_trywait()则是sem_wait()的非阻塞版本,当条件不满足时(信号量为0时),该函数直接返回EAGAIN错误而不会阻塞等待。
sem_timedwait()功能与sem_wait()类似,只是在指定的abs_timeout时间内等待,超过时间则直接返回ETIMEDOUT错误。
【sem_post函数】:
该函数用于以原子操作的方式将信号量的值加1,其原型如下:
与sem_wait一样,sem指向的对象是由sem_init调用初始化的信号量。调用成功时返回0,失败返回-1。
【sem_getvalue函数】:
该函数返回当前信号量的值,通过restrict输出参数返回。如果当前信号量已经上锁(即同步对象不可用),那么返回值为0,或为负数,其绝对值就是等待该信号量解锁的线程数。
【实例1】:
【实例2】:
之所以称为命名信号量,是因为它有一个名字、一个用户ID、一个组ID和权限。这些是提供给不共享内存的那些进程使用命名信号量的接口。命名信号量的名字是一个遵守路径名构造规则的字符串。
【sem_open函数】:
该函数用于创建或打开一个命名信号量,其原型如下:
参数name是一个标识信号量的字符串。参数oflag用来确定是创建信号量还是连接已有的信号量。
oflag的参数可以为0,O_CREAT或O_EXCL:如果为0,表示打开一个已存在的信号量;如果为O_CREAT,表示如果信号量不存在就创建一个信号量,如果存在则打开被返回,此时mode和value都需要指定;如果为O_CREAT|O_EXCL,表示如果信号量存在则返回错误。
mode参数用于创建信号量时指定信号量的权限位,和open函数一样,包括:S_IRUSR、S_IWUSR、S_IRGRP、S_IWGRP、S_IROTH、S_IWOTH。
value表示创建信号量时,信号量的初始值。
【sem_close函数】:
该函数用于关闭命名信号量:
单个程序可以用sem_close函数关闭命名信号量,但是这样做并不能将信号量从系统中删除,因为命名信号量在单个程序执行之外是具有持久性的。当进程调用_exit、exit、exec或从main返回时,进程打开的命名信号量同样会被关闭。
【sem_unlink函数】:
sem_unlink函数用于在所有进程关闭了命名信号量之后,将信号量从系统中删除:
【信号量操作函数】:
与无名信号量一样,操作信号量的函数如下:
命名信号量是随内核持续的。当命名信号量创建后,即使当前没有进程打开某个信号量,它的值依然保持,直到内核重新自举或调用sem_unlink()删除该信号量。
无名信号量的持续性要根据信号量在内存中的位置确定:
很多时候信号量、互斥量和条件变量都可以在某种应用中使用,那这三者的差异有哪些呢?下面列出了这三者之间的差异:
/*编译命令:gcc -o shm shm.c -g */2
3#include<sys/sem.h>
4#include<sys/ipc.h>
5
6#define SEGSIZE 1024
7#define READTIME 1
8
9union semum
10{
11int val
12struct semid_ds *buf
13unsigned short *array
14}arg
15
16/* 创建信号量 */
17int sem_creat(key_t key)
18{
19union semun sem
20int semid
21sem.val = 0
22semid = semget(key, 1, IPC_CREAT | 0666)
23
24if (semid == -1)
25{
26printf("Create semaphore error\n")
27exit(-1)
28}
29
30semctl(semid, 0, SETVAL, sem)
31
32return semid
33}
34
35/* 删除信号量*/
36int del_sem(int semid)
37{
38union semun sem
39sem.val = 0
40semctl(semid, 0, IPC_RMID, sem)
41}
42
43/* 信号量的P操作,使得信号量的值加1 */
44int p(int semid)
45{
46struct sembuf sops = {0,
47 +1,
48 IPC_NOWAIT
49 }
50
51return (semop(semid, &sops, 1))
52}
53
54/* 信号量的v操作,使得信号量的值减1 */
55int v(int semid)
56{
57struct sembuf sops = {0,
58 -1,
59 IPC_NOWAIT
60 }
61
62return (semop(semid, &sops, 1))
63}
64
65/* server主程序 */
66int main(int argc, char **argv)
67{
68key_tkey
69int shmid, semid
70char *shm
71char msg[7] = "-data-"
72char i
73struct semid_ds buf
74
75key = ftok("/", 0)
76shmid = shmget(key, SEGSIZE, IPC_CREAT|0604)
77
78if shmid == -1)
79{
80printf(" create shared memory error\n")
81return -1
82}
83
84shm = (char *)shmat(shmid, 0, 0)
85if (-1 == (int)shm)
86{
87printf(" attach shared memory error\n")
88return -1
89}
90
91semid = sem_creat(key)
92
93for (i = 0i <= 3i++)
94{
95sleep(1)
96p(semid)
97sleep(READTIME)
98msg[5] = '0' + i
99memcpy(shm,msg,sizeof(msg))
100sleep(58)
101v(semid)
102}
103
104shmdt(shm)
105
106shmctl(shmid,IPC_RMID,&buf)
107
108del_sem(semid)
109
110return 0
111
112}
113
114
115
116
117
118
119
120
121
欢迎分享,转载请注明来源:夏雨云
评论列表(0条)