> 文章列表 > linux 信号量semget/semop/semctl

linux 信号量semget/semop/semctl

linux 信号量semget/semop/semctl

  

  • 专栏内容:linux下并发编程
  • 个人主页:我的主页
  • 座右铭:天行健,君子以自强不息;地势坤,君子以厚德载物.

目录

前言

概述

原理机制

接口说明

代码演示

结尾


前言

本专栏主要分享linux下并发编程相关知识,包括多进程,多线程,进程/线程间通信,并发同步控制,以及高并发下性能提升,请大家多多留言。


概述

信号量提供了多任务间的同步机制,生产者产生后,消费者才能消费,避免消费者提前消费的问题。

原理机制

信号量通过对信号量值的增,减操作来达到同步,也就是通常所说的P,V操作原语。一般P操作会消费信号量值,当信号量值不够消费时就会阻塞等待,V操作会增加信号量值,当信号量值达到上限时就是阻塞等待。

限制说明:

对于信号量,操作系统提供以下几个值来限制数量。

SEMMIN  整个操作系统,信号量id的最大数量

SEMMSL  每个信号量id下,最大的信号量个数

SEMMNS 整个操作系统中信号量的最大数量,当然也受上面两个值的约束

以上值可以查看 /proc/sys/kernel/sem

里面值的含义分别为:

 SEMMSL  同上

 SEMMNS 同上 , 大概是SEMMIN  * SEMMNI 

 SEMOPM  op操作中可以操作的,信号量数量的最大值

 SEMMNI  同上

[senllang@localhost semphore]$ cat /proc/sys/kernel/sem

32000   1024000000      500     32000

接口说明

/* 头文件 */#include <sys/types.h>#include <sys/ipc.h>#include <sys/sem.h>

      /* 创建信号量 */

      int semget(key_t key, int nsems, int semflg);

     key, 使用 IPC_PRIVATE, 或者调用ftok产生;

     nsems, 获取或创建的数量,一般都为1;

      semflg, 与其它IPC的flag类似,可以设置为IPC_CREAT若不存在时可以创建,如果或IPC_EXCL值,存在时就会报错。

      /* 设置或删除信号量  */

      int semctl(int semid, int semnum, int cmd, ...);

其中cmd可以取值 :

IPC_RMID,删除semid对应的信号量,同时唤醒所有semop等待

IPC_STAT,获取信号量信息,最后用以下结构作为最后一个参数进行传出。

 struct semid_ds {struct ipc_perm sem_perm;  /* Ownership and permissions */time_t          sem_otime; /* Last semop time */time_t          sem_ctime; /* Last change time */unsigned long   sem_nsems; /* No. of semaphores in set */};

IPC_SET,设置权限等信息,最一个参数用以下结构传入。

 struct ipc_perm {key_t          __key; /* Key supplied to semget(2) */uid_t          uid;   /* Effective UID of owner */gid_t          gid;   /* Effective GID of owner */uid_t          cuid;  /* Effective UID of creator */gid_t          cgid;  /* Effective GID of creator */unsigned short mode;  /* Permissions */unsigned short __seq; /* Sequence number */};

    

    /* 信号量操作 */

   int semop(int semid, struct sembuf *sops, size_t nsops);

     int semtimedop(int semid, struct sembuf *sops, size_t nsops,

                      const struct timespec *timeout);

    有两种op操作接口,一种不带时间,一种可以设置超时时间。

 struct sembuf有三个成员,分别为:

unsigned short sem_num;  /* semaphore number */
short          sem_op;   /* semaphore operation */
short          sem_flg;  /* operation flags */

sem_flag 可以取值 :

IPC_NOWAIT,不会等待;

 SEM_UNDO,在进程结束时,会回退op操作

sem_op, 当>0时会增加信号值;当=0时,会等零值时才返回;当<0时,会减少信号量值,不足时会等待;

代码演示

这个程序创建一个包含 1 个信号量的集合,初始化信号量的值为 1,然后创建一个子进程。子进程等待信号量,然后输出一条信息,最后增加信号量的值。父进程也等待信号量,输出一条信息,然后增加信号量的值;最后销毁信号量。

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/sem.h>int main(int argc, char const *argv[])
{int semid;// 创建一个包含 1 个信号量的集合semid = semget(IPC_PRIVATE, 1, 0666);if (semid == -1) {perror("semget");exit(EXIT_FAILURE);}// 初始化信号量的值为 1if (semctl(semid, 0, SETVAL, 1) == -1) {perror("semctl");exit(EXIT_FAILURE);}// 创建一个子进程if (fork() == 0) {struct sembuf sops;// 子进程等待信号量sops.sem_num = 0;sops.sem_op = -1;sops.sem_flg = 0;if (semop(semid, &sops, 1) == -1) {perror("semop");exit(EXIT_FAILURE);}printf("Child process is working\\n");// 子进程增加信号量的值sops.sem_num = 0;sops.sem_op = 1;sops.sem_flg = 0;if (semop(semid, &sops, 1) == -1) {perror("semop");exit(EXIT_FAILURE);}exit(EXIT_SUCCESS);} else {struct sembuf sops;// 父进程等待信号量sops.sem_num = 0;sops.sem_op = -1;sops.sem_flg = 0;if (semop(semid, &sops, 1) == -1) {perror("semop");exit(EXIT_FAILURE);}printf("Parent process is working\\n");// 父进程增加信号量的值sops.sem_num = 0;sops.sem_op = 1;sops.sem_flg = 0;if (semop(semid, &sops, 1) == -1) {perror("semop");exit(EXIT_FAILURE);}// 销毁信号量if (semctl(semid, 0, IPC_RMID, 0) == -1) {perror("semctl");exit(EXIT_FAILURE);}exit(EXIT_SUCCESS);}return 0;
}

输出结果如下:

[senllang@localhost semphore]$ gcc ex01_sem.c
[senllang@localhost semphore]$ ./a.out
Parent process is working
Child process is working
semop: Invalid argument
 


结尾

作者邮箱:study@senllang.onaliyun.com
如有错误或者疏漏欢迎指出,互相学习。另外有什么想要了解的内容,也可以给我发邮件,互相谈讨,定知无不言。

注:未经同意,不得转载!