UNIX中信号量
来源:互联网 发布:c语言不输出无意义的0 编辑:程序博客网 时间:2024/06/01 11:20
信号量分有名和无名信号量。它们的区别和管道及命名管道的区别类似。有名信号量要求创建一个文件,而无名信号量则直接保存在内存中。
一,Posix信号量
Posex信号量接口总结(见下图):
上面一行是有名信号量,可于fifo相类比,其值保存在文件中,可用于进程和线程同步;
下面一行是无名信号量,可与pipe相类比,其值保存在内存中,可用于进程和线程同步;
中间部分,是两者的公用接口。
1.公共接口
1.1 接口函数说明
#include <semaphore.h>
int sem_wait(sem_t *sem);
测试所指定信号量的值,它的操作是原子的。
若sem>0,那么它减1并立即返回。
若sem==0,则睡眠直到sem>0,此时立即减1,然后返回。
int sem_trywait(sem_t *sem);
其他的行为和sem_wait一样,除了:
若sem==0,不是睡眠,而是返回一个错误EAGAIN。
int sem_post(sem_t *sem);
把指定的信号量sem的值加1;
呼醒正在等待该信号量的任意线程。
int sem_getvalue(sem_t *sem, int *sval);
取回信号量sem的当前值,把该值保存到sval中。
若有1个或更多的线程或进程调用sem_wait阻塞在该信号量上,该函数返回两种值:
1) 返回0
2) 返回阻塞在该信号量上的进程或线程数目
linux采用返回的第一种策略。
注意:在这些函数中,只有sem_post是信号安全的函数,它是可重入函数。
1.2 接口使用的一般流程
sem_init(&sem);
sem_wait(&sem);
critical area;
sem_post(&sem);
remainder area
2.无名信号量
无名信号量是保存在变量类型为sem_t的内存中。
int sem_init(sem_t *sem, int pshared, unsigned int value);
1)pshared==0 用于同一多线程的同步;
2)若pshared>0 用于多个进程间的同步,此时sem必须放在共享内存中。
int sem_destroy(sem_t *sem);
只能销毁由sem_init初始化的信号量,否则后果不可预料也。
例1:
多线程使用信号量的简单例子:
说明:该例子来自于usp。
可以把sem_wait和sme_post调用去掉,看看效果,可以看到出现了交叉输出的情况。
nanosleep调用只是为了让输出的效果更明显,没有其他意义。
更多的例子见mypxsem/prodcons2-4.c
3. 有名信号量
有名信号量是把信号量的值保存在文件中,所以它可以用于线程也可以用于进程间的同步。
如下面的形式:
3.1 常用函数说明
sem_t *sem_open(const char *name, int oflag,
mode_t mode, unsigned int value);
返回一个sem_t类型的指针。该指针随后可用作sem_close等的参数。
该函数参数的详细信息,可以参考手册。
int sem_close(sem_t *sem);
关闭sem信号量,并释放资源。
int sem_unlink(const char *name);
在所有进程关闭信号量后删除name的信号量
3.2 有名信号量的使用
例子:
以上代码创建了一个进程链,若把sem_wait和sem_post调用去掉,可以看到输出很混乱。
这是由于每个子进程都共享了父进程的文件表项,而且都指向打开的文件表项。
system v 信号量
===============
1, 该类信号量,与posix信号量不同。它表示的信号量集,而不是单个信号量。
可用于不同进程间的同步。
内核为每个信号量集,维护一个如下的信息结构:<sys/sem.h>
2, 信号量操作函数
a. 创建和打开信号量
int semget(key_t key, int nsems, int oflag)
(1) nsems>0 : 创建一个信的信号量集,指定集合中信号量的数量,一旦创建就不能更改。
(2) nsems==0 : 访问一个已存在的集合
(3) 返回的是一个称为信号量标识符的整数,semop和semctl函数将使用它。
(4) 创建成功后一下结构被设置:
.sem_perm 的uid和gid成员被设置成的调用进程的有效用户ID和有效组ID
.oflag 参数中的读写权限位存入sem_perm.mode
.sem_otime 被置为0,sem_ctime被设置为当前时间
.sem_nsems 被置为nsems参数的值
.而于该集合中的每个信号量不初始化,这些结构是在semctl,用参数SET_VAL,SETALL初始化的。
b. 设置信号量的值
int semop(int semid, struct sembuf *opsptr, size_t nops);
(1) semid 是semget返回的semid
(2) nops : 是数组opsptr的个数
(3) opsptr : 是操作结构的数组
(4) 若sem_op 是正数,其值就加到semval上;
若sem_op 是0,那么调用者希望等到semval变为0,如果semval是0就反回;
若sem_op 是负数,那么调用者希望等待semval变为大于或等于sem_op的绝对值.
(5) sem_flg
SEM_UNDO 由进程自动释放信号量
IPC_NOWAIT 不阻塞
c. 对信号量集实行控制操作
int semctl(int semid, int semnum, int cmd, ../* union semun arg */);
其中semid是信号量集合,semnum是信号在集合中的序号,
cmd是控制命令,参数可选
cmd取值如下:
GETVAL, SETVAL : semid集合中semnum信号量当前的semval值
GETALL,SETALL :semid集合中所有信号量的值。
IPC_RMID:删除semid信号量集
GETPID:返回最后成功操作该信号的进程号。
IPC_STAT:返回semid集合中的struct semid_ds结构。
例子:
一,Posix信号量
Posex信号量接口总结(见下图):
上面一行是有名信号量,可于fifo相类比,其值保存在文件中,可用于进程和线程同步;
下面一行是无名信号量,可与pipe相类比,其值保存在内存中,可用于进程和线程同步;
中间部分,是两者的公用接口。
- sem_open() sem_close(),sem_unlink() //有名信号量
- \ |sem_wait(),sem_post() |/
- / |sem_trywait(),sem_getvalue()|\sem_destroy() //无名信号量
- sem_init()
1.公共接口
1.1 接口函数说明
#include <semaphore.h>
int sem_wait(sem_t *sem);
测试所指定信号量的值,它的操作是原子的。
若sem>0,那么它减1并立即返回。
若sem==0,则睡眠直到sem>0,此时立即减1,然后返回。
int sem_trywait(sem_t *sem);
其他的行为和sem_wait一样,除了:
若sem==0,不是睡眠,而是返回一个错误EAGAIN。
int sem_post(sem_t *sem);
把指定的信号量sem的值加1;
呼醒正在等待该信号量的任意线程。
int sem_getvalue(sem_t *sem, int *sval);
取回信号量sem的当前值,把该值保存到sval中。
若有1个或更多的线程或进程调用sem_wait阻塞在该信号量上,该函数返回两种值:
1) 返回0
2) 返回阻塞在该信号量上的进程或线程数目
linux采用返回的第一种策略。
注意:在这些函数中,只有sem_post是信号安全的函数,它是可重入函数。
1.2 接口使用的一般流程
sem_init(&sem);
sem_wait(&sem);
critical area;
sem_post(&sem);
remainder area
2.无名信号量
无名信号量是保存在变量类型为sem_t的内存中。
int sem_init(sem_t *sem, int pshared, unsigned int value);
1)pshared==0 用于同一多线程的同步;
2)若pshared>0 用于多个进程间的同步,此时sem必须放在共享内存中。
int sem_destroy(sem_t *sem);
只能销毁由sem_init初始化的信号量,否则后果不可预料也。
例1:
多线程使用信号量的简单例子:
- /*
- * simple_sem_app.c
- */
- #include "all.h"
- /* 每个字符输出的间隔时间 */
- #define TEN_MILLION 5000000L
- #define BUFSIZE 1024
- void *threadout(void *args);
- int main(int argc, char *argv[])
- {
- int error;
- int i;
- int n;
- sem_t semlock;
- pthread_t *tids;
- if (argc != 2) {
- fprintf (stderr, "Usage: %s numthreads\n", argv[0]);
- return 1;
- }
- n = atoi(argv[1]);
- tids = (pthread_t *)calloc(n, sizeof(pthread_t));
- if (tids == NULL) {
- perror("Failed to allocate memory for thread IDs");
- return 1;
- }
- if (sem_init(&semlock, 0, 1) == -1) {
- perror("Failed to initialize semaphore");
- return 1;
- }
- for (i = 0; i < n; i++) {
- if (error = pthread_create(tids + i, NULL, threadout, &semlock)) {
- fprintf(stderr, "Failed to create thread:%s\n", strerror(error));
- return 1;
- }
- }
- for (i = 0; i < n; i++) {
- if (error = pthread_join(tids[i], NULL)) {
- fprintf(stderr, "Failed to join thread:%s\n", strerror(error));
- return 1;
- }
- }
- return 0;
- }
- void *threadout(void *args)
- {
- char buffer[BUFSIZE];
- char *c;
- sem_t *semlockp;
- struct timespec sleeptime;
- semlockp = (sem_t *)args;
- sleeptime.tv_sec = 0;
- sleeptime.tv_nsec = TEN_MILLION;
- snprintf(buffer, BUFSIZE, "This is thread from process %ld\n",
- (long)getpid());
- c = buffer;
- /****************** entry section *******************************/
- while (sem_wait(semlockp) == -1)
- if(errno != EINTR) {
- fprintf(stderr, "Thread failed to lock semaphore\n");
- return NULL;
- }
- /****************** start of critical section *******************/
- while (*c != '\0') {
- fputc(*c, stderr);
- c++;
- nanosleep(&sleeptime, NULL);
- }
- /****************** exit section ********************************/
- if (sem_post(semlockp) == -1)
- fprintf(stderr, "Thread failed to unlock semaphore\n");
- /****************** remainder section ***************************/
- return NULL;
- }
说明:该例子来自于usp。
可以把sem_wait和sme_post调用去掉,看看效果,可以看到出现了交叉输出的情况。
nanosleep调用只是为了让输出的效果更明显,没有其他意义。
更多的例子见mypxsem/prodcons2-4.c
3. 有名信号量
有名信号量是把信号量的值保存在文件中,所以它可以用于线程也可以用于进程间的同步。
如下面的形式:
- sem_t *mutex;
- ...
- mutex = sem_open(pathname, O_CREAT | O_EXCL, FILE_MODE, 0);
- if ((childpid = fork()) == 0) {
- /* child */
- ...
- sem_wait(mutext);
- ...
- }
- /* parent */
- ...
- sem_post(mutex);
- ...
3.1 常用函数说明
sem_t *sem_open(const char *name, int oflag,
mode_t mode, unsigned int value);
返回一个sem_t类型的指针。该指针随后可用作sem_close等的参数。
该函数参数的详细信息,可以参考手册。
int sem_close(sem_t *sem);
关闭sem信号量,并释放资源。
int sem_unlink(const char *name);
在所有进程关闭信号量后删除name的信号量
3.2 有名信号量的使用
例子:
- /*
- * chainname.c
- */
- #include "my_unpipc.h"
- #define BUFSIZE 1024
- #define PERMS (mode_t)(S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)
- #define FLAGS (O_CREAT | O_EXCL)
- static int getnamed(char *name, sem_t **sem, int val);
- int main (int argc, char *argv[]) {
- char buffer[BUFSIZE];
- char *c;
- pid_t childpid = 0;
- int delay;
- volatile int dummy = 0;
- int i, n;
- sem_t *semlockp;
- if (argc != 4){ /* check for valid number of command-line arguments */
- fprintf (stderr, "Usage: %s processes delay semaphorename\n", argv[0]);
- return 1;
- }
- n = atoi(argv[1]);
- delay = atoi(argv[2]);
- for (i = 1; i < n; i++)
- if ((childpid = fork()) > 0) /* father break */
- break;
- snprintf(buffer, BUFSIZE,
- "i:%d process ID:%ld parent ID:%ld child ID:%ld\n",
- i, (long)getpid(), (long)getppid(), (long)childpid);
- c = buffer;
- if (getnamed(argv[3], &semlockp, 1) == -1) {
- perror("Failed to create named semaphore");
- return 1;
- }
- while (sem_wait(semlockp) == -1) /* entry section */
- if (errno != EINTR) {
- perror("Failed to lock semlock");
- return 1;
- }
- while (*c != '\0') { /* critical section */
- fputc(*c, stderr);
- c++;
- for (i = 0; i < delay; i++)
- dummy++;
- }
- if (sem_post(semlockp) == -1) { /* exit section */
- perror("Failed to unlock semlock");
- return 1;
- }
- if (wait(NULL) == -1) /* remainder section */
- return 1;
- return 0;
- }
- static int getnamed(char *name, sem_t **sem, int val)
- {
- while (((*sem = sem_open(name, FLAGS , PERMS, val)) == SEM_FAILED) &&
- (errno == EINTR)) ;
- if (*sem != SEM_FAILED)
- return 0;
- if (errno != EEXIST)
- return -1;
- while (((*sem = sem_open(name, 0)) == SEM_FAILED) && (errno == EINTR)) ;
- if (*sem != SEM_FAILED)
- return 0;
- return -1;
- }
以上代码创建了一个进程链,若把sem_wait和sem_post调用去掉,可以看到输出很混乱。
这是由于每个子进程都共享了父进程的文件表项,而且都指向打开的文件表项。
system v 信号量
===============
1, 该类信号量,与posix信号量不同。它表示的信号量集,而不是单个信号量。
可用于不同进程间的同步。
内核为每个信号量集,维护一个如下的信息结构:<sys/sem.h>
- struct semid_ds {
- struct ipc_perm sem_perm; /* 信号量集的操作许可权限 */
- struct sem *sem_base; /* 某个信号量sem结构数组的指针,
- 当前信号量集中的每个信号量对应其中一个数组元素 */
- ushort sem_nsems; /* sem_base 数组的个数 */
- time_t sem_otime; /* 最后一次成功修改信号量数组的时间 */
- time_t sem_ctime; /* 成功创建时间 */
- };
- struct sem {
- ushort semval; /* 信号量的当前值 */
- short sempid; /* 最后一次返回该信号量的进程ID号 */
- ushort semncnt; /* 等待semval大于当前值的进程个数 */
- ushort semzcnt; /* 等待semval变成0的进程个数 */
- };
2, 信号量操作函数
a. 创建和打开信号量
int semget(key_t key, int nsems, int oflag)
(1) nsems>0 : 创建一个信的信号量集,指定集合中信号量的数量,一旦创建就不能更改。
(2) nsems==0 : 访问一个已存在的集合
(3) 返回的是一个称为信号量标识符的整数,semop和semctl函数将使用它。
(4) 创建成功后一下结构被设置:
.sem_perm 的uid和gid成员被设置成的调用进程的有效用户ID和有效组ID
.oflag 参数中的读写权限位存入sem_perm.mode
.sem_otime 被置为0,sem_ctime被设置为当前时间
.sem_nsems 被置为nsems参数的值
.而于该集合中的每个信号量不初始化,这些结构是在semctl,用参数SET_VAL,SETALL初始化的。
b. 设置信号量的值
int semop(int semid, struct sembuf *opsptr, size_t nops);
(1) semid 是semget返回的semid
(2) nops : 是数组opsptr的个数
(3) opsptr : 是操作结构的数组
- struct sembuf {
- short sem_num; /* 信号量的数目: 0,1,...,nsems-1 */
- short sem_op; /* 信号量操作 */
- short sem_flg; /* 操作表示符 */
- };
(4) 若sem_op 是正数,其值就加到semval上;
若sem_op 是0,那么调用者希望等到semval变为0,如果semval是0就反回;
若sem_op 是负数,那么调用者希望等待semval变为大于或等于sem_op的绝对值.
(5) sem_flg
SEM_UNDO 由进程自动释放信号量
IPC_NOWAIT 不阻塞
c. 对信号量集实行控制操作
int semctl(int semid, int semnum, int cmd, ../* union semun arg */);
其中semid是信号量集合,semnum是信号在集合中的序号,
- union semun
- {
- int val; /* cmd == SETVAL */
- struct semid_ds *buf /* cmd == IPC_SET或者 cmd == IPC_STAT */
- ushort *array; /* cmd == SETALL, 或 cmd = GETALL */
- };
cmd是控制命令,参数可选
cmd取值如下:
GETVAL, SETVAL : semid集合中semnum信号量当前的semval值
GETALL,SETALL :semid集合中所有信号量的值。
IPC_RMID:删除semid信号量集
GETPID:返回最后成功操作该信号的进程号。
IPC_STAT:返回semid集合中的struct semid_ds结构。
例子:
- /* my_sem.c */
- #include <sys/types.h>
- #include <sys/sem.h>
- #include <sys/ipc.h>
- #include <string.h>
- #include <errno.h>
- #include <unistd.h>
- #include <stdio.h>
- int main (int argc, char **argv)
- {
- key_t ipckey;
- int semid;
- /*建立两个信号灯结构*/
- struct sembuf sem[2]; /* sembuf defined in sys/sem.h */
- /* 创建IPC Key */
- ipckey = ftok("/tmp/rich", 42);
- /* 创建信号量. 4 == READ, 2 == ALTER */
- semid = semget(ipckey, 1, 0666 | IPC_CREAT);
- if (semid < 0)
- {
- printf("Error - %sn", strerror(errno));
- _exit(1);
- }
- /*设置*/
- /* These never change so leave them outside the loop */
- sem[0].sem_num = 0;
- sem[1].sem_num = 0;
- sem[0].sem_flg = SEM_UNDO; /* Release semaphore on exit */
- sem[1].sem_flg = SEM_UNDO; /* Release semaphore on exit */
- while(1)
- {
- printf("[%s] Waiting for the semaphore to be releasedn\n", argv[1]);
- /* 设置两个信号灯,灯1等待,灯2请求资源锁 */
- sem[0].sem_op = 0; /* Wait for zero */
- sem[1].sem_op = 1; /* Add 1 to lock it*/
- /*设置信号量集,两个信号量*/
- semop(semid, sem, 2);
- /*资源锁区*/
- printf("[%s] I have the semaphoren\n", argv[1]);
- sleep(rand() % 3);
- /* Critical section, sleep for 0-2 seconds */
- sem[0].sem_op = -1; /* Decrement to unlock */
- /*出锁,对信号量1操作*/
- semop(semid, sem, 1);
- printf("[%s] Released semaphoren\n", argv[1]);
- sleep(rand() % 3); /* Sleep 0-2 seconds */
- }
- }
0 0
- UNIX中信号量
- 【转】Unix信号量中基础知识介绍
- Unix信号量
- UNIX信号量函数
- Unix的信号量
- linux/unix信号量的值
- UNIX网络编程:信号量semphore
- 【C语言】【unix c】信号量
- UNIX多用户系统下信号量操作详解
- 转载:UNIX多用户系统下信号量操作详解
- UNIX多用户系统下信号量操作详解
- UNIX多用户系统下信号量操作详解
- Windows/Unix 信号量(Semaphore)示例程序
- Unix/Linux下的IPC---信号量集
- UNIX网络编程(十)Posix信号量
- UNIX网络编程——Posix信号量
- UNIX网络编程:IPC之信号量
- Java中信号量 Semaphore
- Android开发:性能最佳实践-管理应用内存
- linux termios结构
- 皇家物流系统
- Oracle 游标(cursor) 说明( cursor (SQL)解析过程)
- 来自一个朋友
- UNIX中信号量
- java访问mysql数据库的方法
- 使用EMOJI表情
- 如何停止iis
- 使用curator实现zookeeper锁服务的示例分享
- 一张图看懂AR至GL数据流
- HDU 1358 Period(kmp)
- 微信分享
- 解析皇家物流系统四大特色