1. 信号量简介

信号量用于进程或线程间同步,Posix信号量是一个非负整型,只有两种操作,加一(sem_post)和减一(sem_wait),如果信号量值为0,sem_wait默认阻塞。

Posix信号量有两种,有名信号量和无名信号量,顾名思义,就是是否有名字。有名信号量有一个名字,长度不超过NAME_MAX-4(i.e. 251),因为内核会默认加上'sem.',所以这里要减4,名字以斜杠开头'/',后面跟上一个或多个非斜杠字符。不同进程可以通过同一个名字来操作有名信号量,sem_open用于创建或者获取已存在的信号量,创建好之后就可以使用sem_post或者sem_wait来操作。使用完之后可以使用sem_close来关闭信号量,sem_unlink用来删除信号量,删除并不立即销毁,只有当所有进程都sem_close才开始销毁。

无名信号量没有名字,基于内存,通常用在同一个进程线程之间或者不同进程的共享内存里,因为同一个进程的不同线程共享进程地址空间,所以可以访问到,放到共享内存里也可以被不同进程访问到。使用前必须使用sem_init进行初始化,初始化之后就可以使用sem_waitsem_post操作,使用完成后sem_destroy接口进行销毁,下一节介绍接口的使用

2. 信号量API接口

2.1 有名信号量创建

#include <fcntl.h>           /* For O_* constants */
#include <sys/stat.h>        /* For mode constants */
#include <semaphore.h>/**
* @brief 创建或获取有名信号量
*
* @params name 有名信号量关联的名字,斜杠开头,长度不超过NAME_MAX-4(e.g. 251)
* @params oflag 标志位,可选值包括O_CREAT| O_EXCL
* 这里如果指定了O_CREAT标志位,还要填写额外两个参数,mode和value
*
* @params mode,参考open函数,通常填0即可
* @params value 信号量的初始值
* @returns 成功返回描述符,失败返回-1
**/sem_t *sem_open(const char *name, int oflag);
sem_t *sem_open(const char *name, int oflag,mode_t mode, unsigned int value);# 编译加上 -pthread选项
Link with -pthread.

2.2 信号量减一操作

#include <semaphore.h>/**
* @brief lock信号量并减1,当信号量大于0,操作可以执行,否则则阻塞。如果设置了NONBLOCK标志位,则报错返回
*
* @params sem 信号量描述符
* @returns 成功返回0,失败返回-1
**/
int sem_wait(sem_t *sem);/**
* @brief 同sem_wait,只不过如果无法减1则立即报错返回
*
**/
int sem_trywait(sem_t *sem);/**
* @brief 同sem_wait,只不过如果无法减1则会等待一段时间,注意这里时间参数要设置正确
*
**/
int sem_timedwait(sem_t *sem, const struct timespec *abs_timeout);# 编译链接选项 -pthread
Link with -pthread.

2.3 信号量加1操作

#include <semaphore.h>/**
* @brief 信号量的值加1
*
* @params sem 信号量文件描述符
* @returns 成功返回0,失败返回-1
**/int sem_post(sem_t *sem);# 编译链接选项 -pthread
Link with -pthread.

2.4 关闭有名信号量

#include <semaphore.h>/**
* @brief 关闭信号量,系统为当前进程分配的信号量资源会被释放。
*
* @params sem 信号量文件描述符
* @returns 成功返回0,失败返回-1
**/int sem_close(sem_t *sem);# 编译链接选项 -pthread
Link with -pthread.

2.5 销毁有名信号量

#include <semaphore.h>/**
* @brief 销毁信号量,只有当所有进程都关闭信号量之后才开始销毁工作
*
* @params name 信号量名字
* @returns 成功返回0,失败返回-1
**/int sem_unlink(const char *name);# 编译链接选项 -pthread
Link with -pthread.

2.6 初始化无名信号量

#include <semaphore.h>/**
* @brief 初始化无名信号量
*
* @params sem 待初始化的信号量地址
* @params pshared 为0表示线程间共享,非0表示进程间共享
* @params value 信号量初始值,不超过SEM_VALUE_MAX
* @returns 成功返回0,失败返回-1
**/int sem_init(sem_t *sem, int pshared, unsigned int value);# 编译链接选项 -pthread
Link with -pthread.

2.7 销毁无名信号量

#include <semaphore.h>/**
* @brief 销毁无名信号量
*
* @params sem 要销毁的信号量
* 如果有其它线程或进程阻塞在sem上,此时销毁会产生未定义的行为
*
* @returns 成功返回0,失败返回-1
**/int sem_destroy(sem_t *sem);# 编译链接选项 -pthread
Link with -pthread.

3 有名信号量例子

3.1 信号量生产者


// 生产者
#include <fcntl.h>           /* For O_* constants */
#include <sys/stat.h>        /* For mode constants */
#include <semaphore.h>
#include <errno.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>#define SEM_NAME "/sem0"int main(int argc, char** argv)
{if (argc < 3){printf("Usage: ./sem_post timeval nums\n");return -1;}int ts = atoi(argv[1]);int total = atoi(argv[2]);if (total < 1 || ts < 1){printf("invalid param\n");return -1;}sem_t* sem_id;// 创建信号量并初始化值为0sem_id = sem_open(SEM_NAME, O_CREAT, O_RDWR, 0);if (sem_id == SEM_FAILED){perror("sem_open error");return -1;}int curr = 0;while (curr < total){        // 生成信号量,即加1while (sem_post(sem_id)){perror("sem_post error, try later");sleep(1);}printf("producing succ\n");sleep(ts);++curr;}printf("work done\n");// 关闭信号量sem_close(sem_id);return 0;
}

3.2 信号量消费者


// 消费者
#include <fcntl.h>           /* For O_* constants */
#include <sys/stat.h>        /* For mode constants */
#include <semaphore.h>
#include <errno.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>#define SEM_NAME "/sem0"int main()
{sem_t* sem_id;// 创建信号量并初始化值为0sem_id = sem_open(SEM_NAME, O_CREAT, O_RDWR, 0);if (sem_id == SEM_FAILED){perror("sem_open error");return -1;}while (1){// 消费信号量if (sem_wait(sem_id)){perror("sem_wait fail, try later\n");sleep(1);continue;}printf("consuming succ\n");}// 关闭信号量sem_close(sem_id);return 0;
}

3.3 编译&运行

default:gcc -o sem_post sem_post.c -pthreadgcc -o sem_wait sem_wait.c -pthread
clean:rm -rf sem_wait sem_post

4. 无名信号量例子

创建两个线程,分别用于生产和消费

#include <semaphore.h>
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>// 消费者
void* consumer_worker(void *arg)
{sem_t *sem = arg;while (1){// 消费信号量if (sem_wait(sem)){perror("[consumer] sem_wait error, try later\n");sleep(1);continue;}printf("[consumer] consume succ\n");}return 0;
}// 生产者
void* producer_worker(void *arg)
{sem_t *sem = arg;while (1){// 生成信号量if (sem_post(sem)){perror("[producer] sem_post error, try later\n");sleep(1);continue;}printf("[producer] produce succ\n");sleep(1);}return 0;
}int main(int argc, char** argv)
{pthread_t consumer, producer;if (argc < 2){printf("Usage: ./unnamed_sem time\n");return -1;}int tm = atoi(argv[1]);if (tm < 1){printf("invalid param\n");return -1;}sem_t sem;// 无名信号量初始化if (sem_init(&sem, 0, 0)){perror("sem_init error");return -1;}// 创建生产者线程if (pthread_create(&producer, NULL, &producer_worker, (void *)&sem)){perror("create producer_worker error");sem_destroy(&sem);return -1;}// 创建消费者线程if (pthread_create(&consumer, NULL, &consumer_worker, (void *)&sem)){perror("create consumer_worker error");sem_destroy(&sem);return -1;}printf("main thread sleep:%d\n", tm);sleep(tm);// 无名信号量销毁sem_destroy(&sem);return 0;
}

5. 参考资料

1. https://www.man7.org/linux/man-pages/man7/sem_overview.7.html

2. 《Linux环境编程 从应用到内核》

================================================================================================

Linux应用程序、内核、驱动、后台开发交流讨论群(745510310),感兴趣的同学可以加群讨论、交流、资料查找等,前进的道路上,你不是一个人奥^_^。

Linux进程间通信五 Posix 信号量简介与示例相关推荐

  1. linux进程间通信:POSIX信号量

    文章目录 概念描述 编程接口 注意事项 编程案例 信号量基本接口使用案例 信号量父子进程间通信 信号量实现 两进程之间通信 概念描述 英文:semaphore 简称SEM,主要用来进行进程间同步 本质 ...

  2. linux进程间通信:POSIX 消息队列 ----异步通信

    在上一篇中linux进程间通信:POSIX 消息队列我们知道消息队列中在消息个数达到了队列所能承载的上限,就会发生消息的写阻塞. 阻塞式的通信影响系统效率,进程之间在通信收到阻塞时并不能去做其他事情, ...

  3. Linux进程间通信三 System V 信号量简介与示例

    1. System V信号量简介 SystemV信号量主要用于解决生产者和消费者问题,一个信号量能够控制多个资源,说它是信号量集也不为过. 2. API接口介绍 2.1 创建或打开信号量集 #incl ...

  4. linux进程间通信:POSIX 共享内存

    文章目录 思维导图 通信原理 优势 POSIX 共享内存 编程接口 编程案例 思维导图 之前学习过sysemV 的共享内存的实现及使用原理,参考linux进程间通信:system V 共享内存 POS ...

  5. Linux进程同步之POSIX信号量

    POSIX信号量是属于POSIX标准系统接口定义的实时扩展部分.在SUS(Single UNIX Specification)单一规范中,定义的XSI IPC中也同样定义了人们通常称为System V ...

  6. linux进程间通信:POSIX 消息队列

    文章目录 基本介绍 相关编程接口 编程实例 消息队列通信实例 消息队列属性设置实例 基本介绍 关于消息队列的基本介绍,前面在学习system V的消息队列时已经有过了解,linux进程间通信:syst ...

  7. linux网络编程-posix信号量与互斥锁(39)

    -posix信号量信号量 是打开一个有名的信号量 sem_init是打开一个无名的信号量,无名信号量的销毁用sem_destroy sem_wait和sem_post是对信号量进行pv操作,既可以使用 ...

  8. Linux进程间通信六 Posix 共享内存简介与示例

    1. 共享内存简介 共享内存主要用于不同进程之间相互通信,因为操作的是同一块地址,不需要内核和用户层之间数据拷贝,属于最快的进程间通信方式,不过,为了防止读写冲突,一般需要额外的同步手段.之前介绍了S ...

  9. Linux进程间通信四 Posix 消息队列简介与示例

    目录 1. Posix 消息队列简介 2. API接口 2.1 创建或打开消息队列 2.2 发送消息 2.3 接收消息 2.4 获取.设置消息队列属性 2.5 关闭消息队列 2.6 删除消息队列 2. ...

最新文章

  1. adams建立一绳索不带滑轮_建立企业精益供应链,必须先解开现有绳索 系列(一)...
  2. 阿里P7面试官告诉你:3-5年以上的Android开发如何深入进阶?Android中高级开发必须掌握哪些?
  3. java解锁_Java 姿势解锁 —— Lists.transform
  4. 从零起步CMFCToolBar用法详解
  5. 程序猜价格c语言,C语言大作业:编写菜单控制猜商品价格程序
  6. linux内核分为子系统,Linux内核内存管理子系统分析【转】
  7. SpringBoot2.1.9 多Kafka消费者配置
  8. mysql以秒为单位限制资源_MYSQL中限制资源的使用
  9. i茅台app上线首日,直接冲到了App Store免费榜第一
  10. 101 Free VMware Tools
  11. 超全现代虚幻UE4素材网站整理
  12. HTML5期末大作业:影视视频网站设计——爱影评在线电影(10页面) HTML+CSS+JavaScript 学生DW网页设计作业成品 web课程设计网页规划与设计 计算机毕设网页设计源码
  13. Python文本处理(3)——文本表示之 one-hot 词向量(1)——纯小白都能懂!
  14. 研发管理应聚焦于价值实现活动的贯穿和闭环
  15. 开始撸lodash源码
  16. 吸血鬼数字java_Java求吸血鬼数算法(通用)
  17. Linux服务器硬件及RAID
  18. 【Java】文如何制作帮助文档
  19. MySQL,刷题之对完整性约束操作,题+代码!!
  20. 产品研发管理的系统解决方案

热门文章

  1. 【Win10 UWP】URI Scheme(二):自定义协议的处理和适用场景
  2. 1010 [HNOI2008]玩具装箱toy
  3. 如何查看开发者账号何时到期
  4. HP 520 双系统 vista xp
  5. 用固定收敛标准特征迭代次数法实现分类是不是一个巧合?
  6. python class 属性是什么_python class 的属性
  7. java只会用不知道原理6_程序员面试宝典之14道初级Java面试题分享
  8. 【Paper】2007_Consensus control for a class of networks of dynamic agents 二阶静态一致性
  9. 第6章-一阶多智体系统一致性-->6.5 带有领航者系统一致性
  10. Buffer Status Report(BSR)