0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

Linux信号量(2):POSIX 信号量

电子设计 来源:电子设计 作者:电子设计 2020-10-29 17:34 次阅读
加入交流群
微信小助手二维码

扫码添加小助手

加入工程师交流群

上一章,讲述了 SYSTEM V 信号量,主要运行于进程之间,本章主要介绍 POSIX 信号量:有名信号量、无名信号量。

POSIX 信号量

POSIX 信号量进程是 3 种 IPC(Inter-Process Communication) 机制之一,3 种 IPC 机制源于 POSIX.1 的实时扩展。Single UNIX Specification 将 3 种机制(消息队列,信号量和共享存储)置于可选部分中。在 SUSv4 之前,POSIX 信号量接口已经被包含在信号量选项中。在 SUSv4 中,这些接口被移至了基本规范,而消息队列和共享存储接口依然是可选的。

POSIX 信号量接口意在解决 XSI 信号量接口的几个缺陷。

相比于 XSI 接口,POSIX 信号量接口考虑了更高性能的实现。

POSIX 信号量使用更简单:没有信号量集,在熟悉的文件系统操作后一些接口被模式化了。尽管没有要求一定要在文件系统中实现,但是一些系统的确是这么实现的。

POSIX 信号量在删除时表现更完美。回忆一下,当一个 XSI 信号量被删除时,使用这个信号量标识符的操作会失败,并将 errno 设置成 EIDRM。使用 POSIX 信号量时,操作能继续正常工作直到该信号量的最后一次引用被释放。

分类

POSIX 信号量是一个 sem_t 类型的变量,但 POSIX 有两种信号量的实现机制:无名信号量和命名信号量。无名信号量只可以在共享内存的情况下,比如实现进程中各个线程之间的互斥和同步,因此无名信号量也被称作基于内存的信号量;命名信号量通常用于不共享内存的情况下,比如进程间通信

同时,在创建信号量时,根据信号量取值的不同,POSIX 信号量还可以分为:

二值信号量:信号量的值只有 0 和 1,这和互斥量很类似,若资源被锁住,信号量的值为 0,若资源可用,则信号量的值为 1;

计数信号量:信号量的值在 0 到一个大于 1 的限制值之间,该计数表示可用的资源的个数。

区别

有名信号量和无名信号量的差异在于创建和销毁的形式上,但是其他工作一样。

无名信号量只能存在于内存中,要求使用信号量的进程必须能访问信号量所在的这一块内存,所以无名信号量只能应用在同一进程内的线程之间(共享进程的内存),或者不同进程中已经映射相同内存内容到它们的地址空间中的线程(即信号量所在内存被通信的进程共享)。意思是说无名信号量只能通过共享内存访问。

相反,有名信号量可以通过名字访问,因此可以被任何知道它们名字的进程中的线程使用。

单个进程中使用 POSIX 信号量时,无名信号量更简单。多个进程间使用 POSIX 信号量时,有名信号量更简单。

联系

无论是有名信号量还是无名信号量,都可以通过以下函数进行信号量值操作。

wait(P)

wait 为信号量值减一操作,总共有三个函数,函数原型如下:


#include

intsem_wait(sem_t*sem);

intsem_trywait(sem_t*sem);

intsem_timedwait(sem_t*sem,conststructtimespec*abs_timeout);

Linkwith-pthread. 这一句表示gcc编译时,要加-pthread.


返回值:
若成功,返回0;若出错,返回 -1

sem_wait 的作用是,若 sem 小于 0 ,则线程阻塞于信号量 sem ,直到 sem 大于 0 ;否则信号量值减 1。

sem_trywait 作用与 sem_wait 相同,只是此函数不阻塞线程,如果 sem 小于 0,直接返回一个错误(错误设置为 EAGAIN )。

sem_timedwait 作用也与 sem_wait 相同,第二个参数表示阻塞时间,如果 sem 小于 0 ,则会阻塞,参数指定阻塞时间长度。abs_timeout 指向一个结构体,这个结构体由从 1970-01-01 00:00:00 +0000 (UTC) 开始的秒数和纳秒数构成。

结构体定义如下:

structtimespec{
time_ttv_sec;/*Seconds*/
longtv_nsec;/*Nanoseconds[0..999999999]*/
};

如果指定的阻塞时间到了,但是 sem 仍然小于 0 ,则会返回一个错误 (错误设置为 ETIMEDOUT )。

post(V)

post 为信号量值加一操作,函数原型如下:


#include
intsem_post(sem_t*sem);
Linkwith-pthread.
返回值:
若成功,返回0;若出错,返回 -1

无名信号量

接口函数

信号量的函数都以 sem_ 开头,线程中使用的基本信号函数有 4 个,他们都声明在头文件 semaphore.h 中,该头文件定义了用于信号量操作的 sem_t 类型:

sem_init

该函数用于创建信号量,原型如下:


intsem_init(sem_t*sem,intpshared,unsignedintvalue);

功能:该函数初始化由 sem 指向的信号对象,设置它的共享选项,并给它一个初始的整数值。pshared 控制信号量的类型,如果其值为 0,就表示信号量是当前进程的局部信号量,否则信号量就可以在多个进程间共享,value 为 sem 的初始值。返回值:该函数调用成功返回 0,失败返回 -1。

sem_destroy

该函数用于对用完的信号量进行清理,其原型如下:


intsem_destroy(sem_t*sem);

返回值:

成功返回 0,失败返回 -1。

sem_getvalue 函数

该函数返回当前信号量的值,通过 restrict 输出参数返回。如果当前信号量已经上锁(即同步对象不可用),那么返回值为 0,或为负数,其绝对值就是等待该信号量解锁的线程数。


intsem_getvalue(sem_t*restrict,int*restrict);

使用实例

【实例 1】:

#include

#include

#include

#include

#include

#include

#include

#include

sem_tsem;

#definehandle_error(msg)do{/
perror(msg);/
exit(EXIT_FAILURE);/
}while(0)
staticvoidhandler(intsig){
write(STDOUT_FILENO,"sem_post()fromhandler/n",24);
if(sem_post(&sem)==-1)
{
write(STDERR_FILENO,"sem_post()failed/n",18);
_exit(EXIT_FAILURE);
}}
intmain(intargc,char*argv[]){
ints;
structtimespects;
structsigactionsa;

if(argc!=3)
{
fprintf(stderr,"Usage:%s/n",argv[0]);
exit(EXIT_FAILURE);
}

if(sem_init(&sem,0,0)==-1)
handle_error("sem_init");

/*EstablishSIGALRMhandler;setalarmtimerusingargv[1]*/
sa.sa_handler=handler;
sigemptyset(&sa.sa_mask);
sa.sa_flags=0;
if(sigaction(SIGALRM,&sa,NULL)==-1)
handle_error("sigaction");

alarm(atoi(argv[1]));

/*Calculaterelativeintervalascurrenttimeplus
numberofsecondsgivenargv[2]*/

if(clock_gettime(CLOCK_REALTIME,&ts)==-1)
handle_error("clock_gettime");

ts.tv_sec+=atoi(argv[2]);

printf("main()abouttocallsem_timedwait()/n");
while((s=sem_timedwait(&sem,&ts))==-1&&errno==EINTR)
continue;/*Restartifinterruptedbyhandler*/

/*Checkwhathappened*/
if(s==-1)
{
if(errno==ETIMEDOUT)
printf("sem_timedwait()timedout/n");
else
perror("sem_timedwait");
}
else
{
printf("sem_timedwait()succeeded/n");
}

exit((s==0)?EXIT_SUCCESS:EXIT_FAILURE);

}

【实例 2】:

#include

#include

#include

#include

#include

#include

#include

#include

sem_tsem;
void*func1(void*arg){
sem_wait(&sem);
int*running=(int*)arg;
printf("threadfunc1running:%d/n",*running);

pthread_exit(NULL);
}
void*func2(void*arg)
{
printf("threadfunc2running./n");
sem_post(&sem);

pthread_exit(NULL);
}
intmain(void)
{
inta=3;
sem_init(&sem,0,0);
pthread_tthread_id[2];

pthread_create(&thread_id[0],NULL,func1,(void*)&a);
printf("mainthreadrunning./n");
sleep(10);
pthread_create(&thread_id[1],NULL,func2,(void*)&a);
printf("mainthreadstillrunning./n");
pthread_join(thread_id[0],NULL);
pthread_join(thread_id[1],NULL);
sem_destroy(&sem);

return0;
}

有名信号量

有时候也叫命名信号量,之所以称为命名信号量,是因为它有一个名字、一个用户 ID、一个组 ID 和权限。这些是提供给不共享内存的那些进程使用命名信号量的接口。命名信号量的名字是一个遵守路径名构造规则的字符串。

接口函数

sem_open 函数

该函数用于创建或打开一个命名信号量,其原型如下:


sem_t*sem_open(constchar*name,intoflag);
sem_t*sem_open(constchar*name,intoflag,mode_tmode,unsignedintvalue);

参数

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 函数

该函数用于关闭命名信号量:


intsem_close(sem_t*);

功能:单个程序可以用 sem_close 函数关闭命名信号量,但是这样做并不能将信号量从系统中删除,因为命名信号量在单个程序执行之外是具有持久性的。当进程调用 _exit、exit、exec 或从 main 返回时,进程打开的命名信号量同样会被关闭。

sem_unlink 函数功能:sem_unlink 函数用于在所有进程关闭了命名信号量之后,将信号量从系统中删除:


intsem_unlink(constchar*name);

信号量操作函数与无名信号量一样。

使用实例

#include
#include
#include
#include
#include
#include
#include
#include
#include
#defineSEM_NAME"/sem_name"
sem_t*p_sem;
void*testThread(void*ptr){
sem_wait(p_sem);
sleep(2);
pthread_exit(NULL);}
intmain(void){
inti=0;
pthread_tpid;
intsem_val=0;
p_sem=sem_open(SEM_NAME,O_CREAT,0555,5);

if(p_sem==NULL)
{
printf("sem_open%sfailed!/n",SEM_NAME);
sem_unlink(SEM_NAME);
return-1;
}

for(i=0;i< 7; i++)
    {
        pthread_create(&pid, NULL, testThread, NULL);
        sleep(1);
        // pthread_join(pid, NULL);  // not needed, or loop
        sem_getvalue(p_sem, &sem_val);
        printf("semaphore value : %d/n", sem_val);
    }
    
    sem_close(p_sem);
    sem_unlink(SEM_NAME);
    
    return 0;
}

命名和无名信号量的持续性

命名信号量是随内核持续的。当命名信号量创建后,即使当前没有进程打开某个信号量,它的值依然保持,直到内核重新自举或调用 sem_unlink()删除该信号量。

无名信号量的持续性要根据信号量在内存中的位置确定:

如果无名信号量是在单个进程内部的数据空间中,即信号量只能在进程内部的各个线程间共享,那么信号量是随进程的持续性,当进程终止时他也就消失了;

如果无名信号量位于不同进程的共享内存区,因此只要该共享内存区仍然存在,该信号量就会一直存在;所以此时无名信号量是随内核的持续性。

信号量 - 互斥量 - 条件变量

很多时候信号量、互斥量和条件变量都可以在某种应用中使用,那这三者的差异有哪些呢?下面列出了这三者之间的差异:

互斥量必须由给它上锁的线程解锁;而信号量不需要由等待它的线程进行挂出,可以在其他进程进行挂出操作;

互斥量要么被锁住,要么被解开,只有这两种状态;而信号量的值可以支持多个进程 / 线程成功的进行 wait 操作;

信号量的挂出操作总是被记住,因为信号量有一个计数值,挂出操作总会将该计数值加 1,然而当条件变量发送一个信号时,如果没有线程等待在条件变量,那么该信号就会丢失。

审核编辑 黄昊宇

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • Linux
    +关注

    关注

    88

    文章

    11627

    浏览量

    217895
  • Posix
    +关注

    关注

    0

    文章

    36

    浏览量

    10052
  • 信号量
    +关注

    关注

    0

    文章

    53

    浏览量

    8738
收藏 人收藏
加入交流群
微信小助手二维码

扫码添加小助手

加入工程师交流群

    评论

    相关推荐
    热点推荐

    RTOS 必学概念:任务、信号量、队列一次搞懂

    如果你刚接触RTOS(实时操作系统),很可能会有这样的困惑:“RTOS和裸机程序到底有什么区别?”“任务是线程吗?为什么要分任务?”“信号量和互斥锁有什么区别,不都是同步手段吗?”“队列是不是就是一
    的头像 发表于 11-17 10:53 173次阅读
    RTOS 必学概念:任务、<b class='flag-5'>信号量</b>、队列一次搞懂

    FreeRTOS和uC/OS-II的功能特性

    FreeRTOS: 核心功能: 任务调度(固定优先级抢占式 + 时间片轮询)、队列、信号量(二进制、计数)、互斥锁(带优先级继承)、软件定时器、事件组(事件标志)、任务通知(高效轻量级的信号量
    发表于 11-17 08:17

    求助,关于RT-Thread 最新版本timer.c rt_timer_check函数问题求解

    ;gt;parameter);之前,是否要开启系统中断,V4.1.1版本保持关闭,v5.1.0版本开启了中断: v5.1.0 v4.1.1 我在某个串口任务实现当中,使用了信号量的机制,在串口接收
    发表于 09-25 07:00

    alloc_socket在不断创建socket时会不断新建信号量和互斥锁,导致内存泄漏,怎么解决?

    alloc_socket在不断创建socket时会不断新建信号量和互斥锁,导致内存泄漏。 修改前: static struct wiz_socket *alloc_socket(void
    发表于 09-10 08:22

    中断发送信号量不能释放等待信号量的线程是怎么回事?

    modbus_uart_input(rt_device_t dev, rt_size_t size) { /* 串口接收到数据后产生中断, 调用此回调函数, 然后发送接收信号量
    发表于 09-10 06:28

    为什么无法接收到信号量

    只有一个解析线程负责接收信号量,永久等待的。有别的几个线程收到数据之后会释放信号量,设备刚开始运行是没问题的,可是运行十几个小时之后就无法接收到信号量了,有没有大佬可以帮忙解决一下
    发表于 09-10 06:21

    【HZ-T536开发板免费体验】—— linux 进程创建

    Linux进程通信方式有这几种: 1。管道 2信号量 3。消息队列 4。共享内存 在本帖子中,我会讲解fork(),exit()系统调用的实践。通过应用编程来实现系统调用。 1,进程创建 打开
    发表于 09-01 20:49

    安科瑞继电器或晶体管输出开关输入信号隔离器

    1. 概述 安科瑞开关输入信号隔离器 ,输入干接点或NAMUR型接近开关信号,继电器或晶体管隔离输出。可通过拨码开关设置输出和输入同相或反相逻辑控制,设置开启或关闭线路故障检测功能。该产品需要独立供电,输入、输出和电源三端隔离
    的头像 发表于 05-20 15:07 424次阅读
    安科瑞继电器或晶体管输出开关<b class='flag-5'>量</b>输入<b class='flag-5'>信号</b>隔离器

    4G模拟对传模块,无线通讯,实时同步模拟信号

    。 一、基本概念 4G模拟对传模块,一种支持第四代移动通信网络技术(4GLTE)的无线通信模块,同时具备模拟信号的传输能力。通过将模拟信号
    的头像 发表于 04-10 16:54 602次阅读

    FreeRTOS(V9.0)中创建信号量的函数都没有被定义,因此用不了,怎么解决

    问题背景:我想要使用信号量,结果查找了整个工程都没有创建信号量的函数。我还以为是我自己移植有问题,因此还特地下载了其他人移植好的工程进行编程。结果也没有创建信号量的函数。不论是二值信号量
    发表于 03-13 09:30

    工业级模拟信号实时传输解决方案分享,你肯定用得到

    一、简介 DTD509F系列模拟信号无线传输器通常是由一个无线信号发射终端和一个无线信号接收终端组成,也可以根据现场实现一点对多点或者多点对一点的
    的头像 发表于 03-08 15:14 780次阅读
    工业级模拟<b class='flag-5'>量</b><b class='flag-5'>信号</b>实时传输解决方案分享,你肯定用得到

    工控自动化领域:数字量信号与模拟信号的差异解析

    在工控自动化的神秘世界里,信号如同传递指令和信息的使者,而数字量信号和模拟信号则是其中的两大主角。它们各自有着独特的 “性格” 和 “使命”,在不同的场景中发挥着关键作用。下面,就让
    的头像 发表于 02-20 09:14 969次阅读

    基于OpenHarmony标准系统的C++公共基础类库案例:Semaphore

    的公共资源变量;创建5个线程,每个线程做5次for循环,for循环的内容是获取无名信号量,并修改公共资源变量;(2)有名信号量使用方法定义1个有名信号量,1个供有名
    的头像 发表于 02-10 18:08 628次阅读
    基于OpenHarmony标准系统的C++公共基础类库案例:Semaphore

    模拟信号的应用和优缺点

    在现代工业自动化、环境监测、科学研究等领域,模拟信号作为一种基本且重要的信号形式,扮演着不可或缺的角色。本文将对模拟信号进行深度解析,探
    的头像 发表于 02-03 11:26 2115次阅读

    安科瑞ARTU/K8 8开关信号采集装置单元

    电源:AC/DC 220V 2 技术参数 16路状态采集 开关信号采集装置 遥信单元  3 外形尺寸 “三遥"定义: 遥信
    的头像 发表于 12-18 14:46 836次阅读
    安科瑞ARTU/K8 8开关<b class='flag-5'>量</b><b class='flag-5'>信号</b>采集装置单元