作者:杨硕,华清远见嵌入式学院讲师。

一、信号灯简介:

Linux支持系统5的信号灯(semaphore),是一种进程间通信的方式,只不过它和管道、FIFO或者共享内存等不一样,信号灯主要用于同步或者互斥对共享资源的访问,它的发明来源于火车运行系统中的“信号灯”,利用信号灯可以实现 “PV操作”这种进程间同步机制。P操作是获得资源,将信号灯的值减1,如果结果不为负则执行完毕,进程获得资源,否则进程睡眠以等待资源别的进程释放资源;V操作则是释放资源,给信号灯的值加1,释放一个因执行P操作而等待的进程。

二、信号灯的两种类型

1、二值信号灯:

最简单的信号灯形式,信号灯的值只能取0或1,类似于互斥锁。

虽然二值信号灯能够实现互斥锁的功能,但两者的关注内容不同。信号灯强调共享资源,只要共享资源可用,其他进程同样可以修改信号灯的值;互斥锁更强调进程,占用资源的进程使用完资源后,必须由进程本身来解锁。

2、 计数信号灯:

信号灯的值可以取任意非负值(当然受内核本身的约束),用来统计资源,其值就代表可用资源的个数。

三、Linux下对信号灯的操作

1、 打开或创建信号灯

对应的系统调用:

#include

#include

#include

int semget(key_t key, int nsems, int sem*);

第一个参数key是一个键值,信号灯集的描述符就由系统范围内唯一的一个键值生成。

key可以由ftok函数生产:

#include

#include

key_t ftok(conST char *pathname, int proj_id);

ftok返回与系统中的路径pathname相对应的一个键值

nsems是信号灯集中信号灯的个数,其最大值取决于具体的系统,如果是0,则代表访问已存在的信号灯集。

sem*是一些标志位,它是IPC_CREAT、IPC_EXCL、IPC_NOWAIT三者与访问权限或的结果,访问权限一般都是0600,代表只有信号灯集的属主才对信号灯集有读写的权限。

semget()如果执行成功,返回与key对应的信号灯集描述字(非负整数,存在于内存之中),失败返回-1,并将错误码置于errno全局变量中。

2、操作信号灯

linux可以增加或减小信号灯的值,相应于对共享资源的释放和占有。

对应的系统调用:

#include

#include

#include

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

semop系统调用可以实现对由semid标志的信号灯集中的某一个指定信号灯的一系列操作。

semid即是semget返回的信号灯描述字。

sops是指向结构体sembuf的指针,可以是这种类型的结构体数组的头指针,数组的每一个sembuf结构都刻画一个在特定信号灯上的操作。

nsops为sops指向数组的大小(有几个sembuf结构体)。

sembuf结构体定义如下:

struct sembuf

{

unsigned short sem_num; /* semaphore number */

short sem_op; /* semaphore operation */

short sem_*; /* operation flags */

};

sem_num对应信号灯集中的信号灯,0代表第一个信号灯。

sem_op的值决定了对sem_num指定的信号灯的三种不同操作:

● sem_op = 0,调用者阻塞等待直到信号灯的值等于0时返回。可以用来测试共享资源是否已用完。

● sem_op > 0,代表进程要申请-sem_op个共享资源。

如果信号灯值sem_val > abs(sem_op),则sem_val = sem_val-abs(sem_op);

否则调用进程睡眠直到sem_val>=abs(sem_op)。当然如果sem_*指定为IPC_NOWAIT,则调用进程立即返回。

● sem_op > 0,代表进程要释放sem_op数量的共享资源。也就是V操作。

sem_*可取0,IPC_NOWAIT以及SEM_UNDO两个标志。

● 0代表阻塞调用

● IPC_NOWAIT代表非阻塞调用

● 如果设置了SEM_UNDO标志,那么在进程结束时,相应的操作将被取消,这是比较重要的一个标志位。如果设置了该标志位,那么在进程没有释放共享资源就退出时,内核将代为释放。如果为一个信号灯设置了该标志,内核都要分配一个 sem_undo结构来记录它,为的是确保以后资源能够安全释放。事实上,如果进程退出了,那么它所占用就释放了,但信号灯值却没有改变,此时,信号灯值反映的已经不是资源占有的实际情况,在这种情况下,问题的解决就靠内核来完成。这有点像僵尸进程,进程虽然退出了,资源也都释放了,但内核进程表中仍然有它的记录,此时就需要父进程调用waitpid来解决问题了。

semop调用成功返回0,失败返回-1,并将错误码置于errno全局变量中。

semop可以同时操作多个信号灯,在实际应用中,对应多种资源的申请或释放。semop保证操作的原子性,这一点尤为重要。尤其对于多种资源的申请来说,要么一次性获得所有资源,要么放弃申请,要么在不占有任何资源情况下继续等待,这样,一方面避免了资源的浪费;另一方面,避免了进程之间由于申请共享资源而造成死锁。

3、 获得或设置信号灯属性:

对应的系统调用:

#include

#include

#include

int semctl(int semid, int semnum, int cmd, union semun arg);

semctl通过具体的cmd操作由semid标志的信号灯集上的由semnum指定的信号灯。

常用的cmd有一下几个:

● IPC_STAT 获取信号灯信息,信息由arg.buf返回;

● GETVAL 返回semnum所代表信号灯的值;

● SETVAL 设置semnum所代表信号灯的值为arg.val;

● IPC_RMID 删除semnum所代表的信号灯

用户需要自己定义联合体semun如下:

union semun {

int val; /* Value for SETVAL */

struct semid_ds *buf; /* Buffer for IPC_STAT, IPC_SET */

unsigned short *Array; /* Array for GETALL, SETALL */

struct seminfo *__buf; /* Buffer for IPC_INFO (Linux-specific) */

};

semctl调用成功返回0,失败返回-1,并将错误码置于errno全局变量中。

四、利用信号灯实现PV操作

1、P操作:申请资源

这里我们封装一个函数down():

/*

* function: ask for resource, P operation

* parameter: sem_id : identifier of a semaphore set;

sem_num : semaphore number

* return value: none

*/

void down(int sem_id, int sem_num)

{

struct sembuf op;

op.sem_num = sem_num;

op.sem_op = -1;

op.sem_* = 0;

semop(sem_id, &op, 1);

}

2、V操作:释放资源

这里我们封装一个函数up():

/*

* function: free resource, V operation

* parameter: sem_id : identifier of a semaphore set;

sem_num : semaphore number

* return value: none

*/

void up(int sem_id, int sem_num)

{

struct sembuf op;

op.sem_num = sem_num;

op.sem_op = 1;

op.sem_* = 0;

semop(sem_id, &op, 1);

}

“本文由华清远见http://www.embedu.org/index.htm提供”

华清远见

linux 心跳灯_Linux下信号灯的使用相关推荐

  1. linux 心跳灯_Linux下点亮第一个LED灯

    第一步:  在window下编写汇编代码,点亮第一颗led灯: .text .global _start _start: /* * */ /*设置GPF4 输出*/ ldr r1, =0x560000 ...

  2. linux spidev 应用_Linux下SPI驱动的移植和应用程序的测试

    Linux2.6.32下SPI驱动的移植如下图所示: 下面需要修改部分内核代码,具体操作如下: 1.  修改arch/arm/mach-s3c2440/mach-mini2440.c文件 在inclu ...

  3. linux python版本_linux下更新Python版本并修改默认版本

    linux下更新Python版本并修改默认版本,有需要的朋友可以参考下. 很多情况下拿到的服务器python版本很低,需要自己动手更改默认python版本 1.从官网下载python安装包(这个版本可 ...

  4. linux mysql 事务_linux下mysql Insert update delete 事务 用户管理

    linux下mysql Insert update delete  事务 用户管理 1.INSERT插入语句格式: INSERT INTO tb_name (字段1, 字段2, ...) VALUES ...

  5. linux mpich配置_Linux下安装MPICH

    Linux下 mpich2安装 1:从MPICH2官网下载源代码,1.0.8,当然如果你使用的windows平台也可以下载http://www.mcs.anl.gov/research /projec ...

  6. linux ttyusb读写_linux下非root用户获得devttyUSB0的读写权限

    linux下非root用户获得devttyUSB0的读写权限 首先查看/dev/ttyUSB0的权限属性,在终端输入:teashaw@http://www.doczj.com/doc/049b1b8e ...

  7. linux mysql8配置文件_Linux下 MySQL8安装教程

    之前我们介绍了 Windows下的MySQL8安装教程,那么Linux下该如何安装呢?本文以CentOS 7 为例,一步一步教你如何在Linux下安装MySQL-8.0.18 文章目录 下载MySQL ...

  8. linux 字符串截取_linux下可执行文件分析

    一 背景 也许大家都遇到过这种场景,就是有二进制代码,比如深度分析下此文件到底是什么格式的图片等,这篇文章就记录我分析下二进制可执行文件的过程,已经自己读写二进制文件的一些坑.分析的二进制执行文件为l ...

  9. linux tomcat守护_linux下非root用户运行tomcat

    # 前言:为什么要使用非root用户运行tomcat root用户启动tomcat有一个严重的问题,那就是tomcat具有root权限. 这意味着你的任何一个页面脚本(html/js)都具有root权 ...

最新文章

  1. 2017已过半,这半年,你累吗?
  2. volatile关键字和Java线程安全问题
  3. 5G 时代,AI 如何破竹而出? | AI ProCon
  4. Command ‘ifconfig‘ not found, but can be installed with: sudo apt install net-tools VM Ubuntu 解决方案
  5. $.ajax()常用属性
  6. 定时任务的时间设置-Cron表达式
  7. Python编程实现后剪枝的CART决策树
  8. 【简单示例:数据库表转XML】
  9. 【小墨mysql】mysql系列之三---事务
  10. 高端存储十面埋伏 华为全闪存系列亮剑出击
  11. 软件测试的技术(互联网篇)
  12. 超尴尬婆婆对儿媳的新婚之夜的指导
  13. 数学猜想验证步骤_猜想验证思想在数学教学中的应用
  14. Apollo Planning决策规划算法代码解析 (17):SPEED_HEURISTIC_OPTIMIZER 速度动态规划下
  15. 微信公众平台的STRUTS
  16. POJ1007 DNA Sorting中英对照翻译与参考解答
  17. OSPF的知识点总结及其扩展
  18. 修复iPhone系统故障导致的黑屏
  19. Codeforces div1+2
  20. JS 中对象的深浅拷贝(ES3、ES5、ES6不同方法底层实现,一文搞清楚深浅拷贝面试常问题)

热门文章

  1. 转自汇编网: 高三老师给大一学生的一封信(感动!)
  2. Android系统之制作开机LOGO
  3. 改进后的速算小游戏(2011211909 苟玲、2011211933 郝怡然)
  4. 交换机MAC地址表的形成过程动态演示
  5. ps怎样将一块地方,覆盖成自己想要替换的东西
  6. 手把手教你集成华为Image Kit图像裁剪功能
  7. Python爬虫入门-fiddler抓取手机新闻评论
  8. Cairo 图形指南 (5) —— 形状与填充
  9. Vue——v-if控制元素是否显示
  10. 编译原理——证明文法的二义性(1)