Linux 下的信号量

2023-05-16

linux下的posix有名信号量的几个要点——博客园

最全面的linux信号量解析 ——csdn blog

Semaphore信号量总结——博客园

sem_timedwait ——csdn blog

一、信号量的概念

信号量的使用主要是用来保护共享资源,使得资源在一个时刻只有一个进程(线程)所拥有。

信号量是一个结构体,信号量的值为正的时候,说明它空闲。所测试的线程可以锁定而使用它。若为0,说明它被占用,测试的线程要进入睡眠队列中,等待被唤醒。

二、信号量的分类

linux下的信号量有很多种,首先分为 内核信号量 和 用户态信号量。

内核信号量    :由内核控制路径使用。

用户态信号量:用户态信号量又分为 posix信号量 和system V信号量。

(system V比较复杂,头文件为#include <sys/sem.h>;

posix信号量比较简单,#include <semaphore.h>)

然后posix信号量又分为有名信号量和无名信号量。

有名信号量:其值保存在文件中, 所以它可以用于线程也可以用于进程间的同步。

无名信号量:其值保存在内存中。Linux 只实现了无名信号量。


三、POSIX信号量

1.无名信号量

无名信号量的创建就像声明一般的变量一样简单,例如:

sem_t   sem_id;

然后再初始化该无名信号量,之后就可以放心使用了。

无名信号量常用于多线程间的同步,同时也用于相关进程间的同步。也就是说,无名信号量必须是多个进程(线程)的共享变量,无名信号量要保护的变量也必须是多个进程(线程)的共享变量,这两个条件是缺一不可的。


首先介绍一个结构体,sem_t,它在semaphore.h中是这样定义的:

typedef struct sem_t_ * sem_t;

struct sem_t_

{

  int value;

  pthread_mutex_t lock;

  HANDLE sem;

#if defined(NEED_SEM)

  int leftToUnblock;

#endif

};

2、 无名信号相关的函数

1〉 sem_init信号量初始化函数

函数原型:

#include <semaphore.h>

int sem_init(sem_t *sem,int pshared,unisigned int value);

参数意义:

创建一个信号灯sem,初始化其值为value。

pshared决定了信号量可以在几个进程之间共享。pshared==0 用于同一多线程的同步;

若pshared>0 用于多个相关进程间的同步(即由fork产生的)。由于Linux还没有实现进程间信号灯的共献享,故此值只能为0。

 

2〉 sem_getvalue

函数原型:

#include<semaphore.h>

int sem_getvalue(sem_t *sem,int *sval);

参数意义:

sem :待查询的信号灯。

取回信号量sem的当前值,把该值保存到sval中。

返回值:

若有1个或更多的线程或进程调用sem_wait阻塞在该信号量上,该函数返回0。

3〉 sem_wait

函数原型:

#include<semaphore.h>

int sem_wait(sem_t *sem);

参数意义:

相当于P操作,即申请资源。这是一个阻塞的函数,测试所指定信号量的值,它的操作是原子的。

返回值:

若sem>0,那么那么申请资源成功,它减1并立即返回,返回值为0。

若sem==0,则返回-1,睡眠直到sem>0,此时立即减1,然后返回。

4〉 sem_trywait

函数原型:

#include<semaphore.h>

int sem_trywait(sem_t *sem);

参数意义:

相当于P操作,即申请资源。这是一个非阻塞的函数,测试所指定信号量的值,它的操作是原子的。

返回值:

若sem>0,那么申请资源成功,它减1并立即返回,返回值为0。

若sem==0,返回-1,与sem_wait不同,不是睡眠,而是返回一个错误EAGAIN。

5〉 sem_timedwait

函数原型:

#include <semaphore.h>

int sem_timedwait(sem_t *sem,const struct timespec *abs_timeout);

参数意义:

abs_timeout 指定一个阻塞的时间上限, abs_timeout 参数指向一个指定绝对超时时刻的结构,这个结果由自 Epoch,1970-01-01 00:00:00 +0000(UTC) 秒数和纳秒数构成。一般取当前时间,再加上允许等待的时间间隔。

返回值:

如果调用时超时时刻已经到点,并且信号量不能立即锁定,那么 sem_timedwait() 将失败于超时,返回值为-1,errno 设置为 ETIMEDOUT。

如果操作能被立即执行,那么返回值为0,sem值减1, sem_timedwait() 永远不会失败于超时错误,而不管 abs_timeout 的值。进一步说,abs_timeout 的验证在此时没有进行。

6〉 sem_post

函数原型:

#include <semaphore.h>

int sem_post(sem_t *sem);

参数意义:

t相当于V操作,释放资源。把指定的信号量sem的值加1;呼醒正在等待该信号量的任意线程。

7〉 sem_destroy

函数原型:

#include <semaphore.h>

int sem_post(sem_t *sem);

参数意义:

sem_destroy 是用来删除信号灯的.

8〉 常见错误类型

a.EINTR

这个调用被信号处理器中断,参看 signal(7)。

b.EINVAL

sem 不是一个有效的信号量。

c.对 sem_trywait() 有如下额外的错误:

EAGAIN

操作不能执行而不阻塞(也就是说,信号量当前值是零)。

d.对 sem_timedwait() 有如下额外的错误:

EINVAL

abs_timeout.tv_nsecs的值小于0,或者大于等于 100 百万。

ETIMEDOUT

调用在信号量锁定之前超时。

3、 有名信号量

有名信号量的特点是把信号量的值保存在文件中。

这决定了它的用途非常广:既可以用于线程,也可以用于相关进程间,甚至是不相关进程。

有名信号量能在进程间共享的原因:

由于有名信号量的值是保存在文件中的,所以对于相关进程来说,子进程是继承了父进程的文件描述符,那么子进程所继承的文件描述符所指向的文件是和父进程一样的,当然文件里面保存的有名信号量值就共享了。

4、 有名信号相关函数

有名信号量在使用的时候,和无名信号量共享sem_wait和sem_post函数。

区别是有名信号量使用sem_open代替sem_init,另外在结束的时候要像关闭文件一样去关闭这个有名信号量。

1〉sem_open

函数原型:

#include <semaphore.h>

sem_t *sem_open(const char *name, int oflag, mode_t mode , int value);

函数功能:

打开一个已存在的有名信号量,或创建并初始化一个有名信号量。一个单一的调用就完成了信号量的创建、初始化和权限的设置。

参数意义:

name是文件的路径名;

Oflag 有O_CREAT或O_CREAT|EXCL两个取值;

mode控制新的信号量的访问权限;

Value指定信号量的初始化值。

当oflag = O_CREAT时,若name指定的信号量不存在时,则会创建一个,而且后面的mode和value参数必须有效。若name指定的信号量已存在,则直接打开该信号量,同时忽略mode和value参数。

当oflag = O_CREAT|O_EXCL时,若name指定的信号量已存在,该函数会直接返回error。

注意:

这里的name不能写成/tmp/aaa.sem这样的格式,因为在linux下,sem都是创建在/dev/shm目录下。你可以将name写成“/mysem”或“mysem”,创建出来的文件都是“/dev/shm/sem.mysem”,千万不要写路径。也千万不要写“/tmp/mysem”之类的。

2〉sem_close

函数原型:

#include <semaphore.h>

sem_t *sem_close(sem_t *sem);

函数功能:

关闭信号量。

3〉sem_unlink

函数原型:

#include <semaphore.h>

sem_t *sem_unlink(sem_t *sem);

函数功能:

删除信号量。

注:在删除信号量之前,要确定所有对这个有名信号量的引用都已经通过sem_close()函数关闭了,然后只需在退出或是退出处理函数中调用sem_unlink()去删除系统中的信号量。如果有任何的处理器或是线程引用这个信号量,sem_unlink()函数不会起到任何的作用。

也就是说,必须是最后一个使用该信号量的进程来执行sem_unlick才有效。因为每个信号灯有一个引用计数器记录当前的打开次数,sem_unlink必须等待这个数为0时才能把name所指的信号灯从文件系统中删除。也就是要等待最后一个sem_close发生。

5、有名信号量在无相关进程间的同步

前面已经说过,有名信号量是位于共享内存区的,那么它要保护的资源也必须是位于共享内存区,只有这样才能被无相关的进程所共享。

在下面这个例子中,服务进程和客户进程都使用shmget和shmat来获取得一块共享内存资源。然后利用有名信号量来对这块共享内存资源进行互斥保护。

四、例子

例一:无名信号量

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <semaphore.h>
#include <sys/time.h>
#include <assert.h>
#include <errno.h>
#include <signal.h>
#include <pthread.h>
#include <time.h>
 
void *thread1(void*);
void *thread2(void*);
void *thread3(void*);
 
int alex = 0;
sem_t sem12;
sem_t sem13;
 
int main(int argc,char **argv)
{
    pthread_t pid1,pid2,pid3;
    printf("--entermain:\n");
    int ret =sem_init(&sem12,0,0);
    if(ret != 0)
    {
        printf("sem12 initFail\n");
        return ret;
    }
    ret = sem_init(&sem13,0,0);
    if(ret != 0)
    {
        printf("sem13 initFailed\n");
        return ret;
    }
    pthread_create(&pid1,NULL,thread1,NULL);
    pthread_create(&pid2,NULL,thread2,NULL);
    pthread_create(&pid3,NULL,thread3,NULL);
    sleep(6);
    sem_destroy(&sem12);
    sem_destroy(&sem13);
    printf("--end:\n");
    exit(0);
}
void *thread1(void *arg)
{
    printf("this is thread1:\n");
    int input;
    printf("put annumber:\n");
    sleep(3);
    scanf("%d",&input);
    printf("the number is%d\n",input);
   
    sem_post(&sem12);
    sem_post(&sem13);
    printf("leavethread1\n");
    pthread_exit(0);
}
 
void *thread2(void *arg)
{
    printf("this isthread2:\n");
    sem_wait(&sem12);
    printf("leavethread2\n");
    pthread_exit(0);
}
 
void *thread3(void *arg)
{
    struct timespec ts;
    printf("this isthread3:\n");
    ts.tv_sec = time(NULL) + 5;
    int s =sem_timedwait(&sem13,&ts);
    if(s == -1)
        printf("thread waittimeout\n");
    printf("leave thread3\n");
    pthread_exit(0);
}


运行结果:


例二:有名信号量

//服务器程序
/*
 * test_manage_named_signal_server.c
 *
 * Created on: 2014/04/02
 *     Author: power_user
 */
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <stdio.h>
#include <semaphore.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#define SHMSZ 27
char SEM_NAME[] = "vik";
int main(int argc,char *argv[])
{
    char ch;
    int shmind;
    key_t key;
    char *shm,*s;
    sem_t *mutex;
 
    //name the shared memory segment
    key = 1000;
    //create & initialize semaphore
    mutex = sem_open(SEM_NAME,O_CREATE,0644,1);
    if(mutex == SEM_FAILED)
    {
       perror("unable tocreate semaphore");
       sem_unlink(SEM_NAME);
       exit(-1);
    }
    //create the shared memory segment withthis key
    shmid =shmget(key,SHMSZ,IPC_CREAT|0666);
    if(shmid < 0)
    {
       perror("failure in shmget");
       exit(-1);
    }
    //attach this segment to virtual memory
    shm = shmat(shmid,NULL,0);
    //start writing into memory
    s = shm;
    for(ch = 'A'; ch <= 'Z'; ch++)
    {
       sem_wait(mutex);
       *s++ = ch;
       sem_post(mutex);
    }
    //the bellow loop could be replaced bybinary semaphore
    while(*shm != '*')
    {
       sleep(1);
    }
    sem_close(mutex);
    sem_unlink(SEM_NAME);
    shmctl(shmid,IPC_RMID,0);
    exit(0);
}
//客户端程序
/*
 * test_manage_named_signal_client.c
 *
 * Created on: 2014/04/02
 *     Author: power_user
 */
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <stdio.h>
#include <semaphore.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#define SHMSZ 27
char SEM_NAME[] = "vik";
int main(int argc,char *argv[])
{
    char ch;
    int shmid;
    key_t key;
    char *shm,*s;
    sem_t *mutex;
 
    //name the shared memory segment
    key = 1000;
    //create the shared memory semaphore
    mutex = sem_open(SEM_NAME,0,0644,0);
    if(mutex == SEM_FAILED)
    {
       perror("reader:unableto excute semaphore ");
       sem_close(mutex);
       exit(-1);
    }
    //create the shared memory segment withthis key
    shmid = shmget(key,SHMSZ,0666);
    if(shmid < 0)
    {
       perror("reader:failure in shmget");
       exit(-1);
    }
    //attach this segment to virtual memory
    shm = shmat(shmid, NULL, 0);
    //start reading
    s = shm;
    for(s = shm; *s != NULL; s++)
    {
       sem_wait(mutex);
       putchar(*s);
       sem_post(mutex);
    }
    //once done signal exiting of reader: Thiscan be repalced by another semaphore
    *shm = '*';
    sem_close(mutex);
    shmctl(shmid, IPC_RMID, 0);
    exit(0);
}
 


 

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Linux 下的信号量 的相关文章

随机推荐

  • FreeRTOS临界段代码

    本文是 ALIENTEK STM32F429 FreeRTOS 开发教程 第四章学习笔记的补充 第一章笔记 FreeRTOS简介与源码下载 第二章笔记 FreeRTOS在STM32F4上移植 第三章笔记 FreeRTOS系统配置 第四章笔记
  • FreeRTOS任务基础

    本文是 ALIENTEK STM32F429 FreeRTOS 开发教程 第五章学习笔记 第一章笔记 FreeRTOS简介与源码下载 第二章笔记 FreeRTOS在STM32F4上移植 第三章笔记 FreeRTOS系统配置 第四章笔记 Fr
  • FreeRTOS任务API函数的使用

    这篇文章最后的demo工程可以在网盘中自行下载 xff1a 链接 xff1a https pan baidu com s 1o1U niMKu0RuDAFio1nKMA 密码 xff1a ysev 本文是 ALIENTEK STM32F42
  • FreeRTOS列表和列表项

    本文是 ALIENTEK STM32F429 FreeRTOS 开发教程 第七章学习笔记 第一章笔记 FreeRTOS简介与源码下载 第二章笔记 FreeRTOS在STM32F4上移植 第三章笔记 FreeRTOS系统配置 第四章笔记 Fr
  • FreeRTOS任务创建

    本文是 ALIENTEK STM32F429 FreeRTOS 开发教程 第八章学习笔记 1 第一章笔记 FreeRTOS简介与源码下载 第二章笔记 FreeRTOS在STM32F4上移植 第三章笔记 FreeRTOS系统配置 第四章笔记
  • FreeRTOS任务调度器开启

    本文是 ALIENTEK STM32F429 FreeRTOS 开发教程 第八章学习笔记 2 第一章笔记 FreeRTOS简介与源码下载 第二章笔记 FreeRTOS在STM32F4上移植 第三章笔记 FreeRTOS系统配置 第四章笔记
  • css样式缓存

    1 问题 css样式变更之后 xff0c 必须手动清缓存才能生效 2 百度 在实际项目开发过过程中 xff0c 页面是上传到服务器上的 而为了减少服务器的压力 xff0c 让用户少加载 xff0c 浏览器会将图片 css js缓存到本地中
  • 使用Verilog HDL语言实现4位超前进位加法器

    一 1位半加器的实现 1 1 原理 半加器由两个一位输入相加 xff0c 输出一个结果位和进位 xff0c 没有进位输入的加法器电路 1 2 真值表 1 3 逻辑表达式 S 61 A B C 61 A amp B 1 4 Verilog 实
  • FreeRTOS任务切换

    本文是 ALIENTEK STM32F429 FreeRTOS 开发教程 第九章学习笔记 第一章笔记 FreeRTOS简介与源码下载 第二章笔记 FreeRTOS在STM32F4上移植 第三章笔记 FreeRTOS系统配置 第四章笔记 Fr
  • 蓝桥杯嵌入式备赛手册

    本文是我参加蓝桥杯嵌入式比赛后的一些心得体会和一些自己总结的驱动代码 xff0c 希望能给以后参加蓝桥杯嵌入式的同学带来一些帮助 本文没有经过校对 xff0c 如有错误还请包涵 xff0c 欢迎大家交流和指正 xff0c 转载请注明出处 一
  • U-Boot详细分析(2)——Exynos4412启动过程

    一 iROM 首先arm的pc指针从0x0地址开始执行 xff0c 打开4412手册可以看到0x0000 0000地址存放着iROM xff1a 所以4412上电后会到iROM中去执行 xff0c iROM是4412出厂时就固化的一段程序
  • U-Boot详细分析(3)——系统时钟操作

    一 引子 在上一篇文章 U Boot详细分析 2 Exynos4412启动过程中可以看到 xff0c BL2程序流程图中有SET CLOCK s这一步骤 xff0c 并且在U Boot源码的 board samsung smdkc210 l
  • 字符串中的第一个唯一字符

    leetcode 的easy题 xff1a 给定一个字符串 xff0c 找到它的第一个不重复的字符 xff0c 并返回它的索引 如果不存在 xff0c 则返回 1 案例 s 61 34 leetcode 34 返回 0 s 61 34 lo
  • WOC广域网加速

    WOC广域网加速 1 广域网存在的问题 xff1a 大量的路由 网关 跨区域和跨运营商的情况比较常见 丢包和延长使用传输效率降低 交互过多导致传输效率差 2 解读方案 xff1a 应用流量可视化 链路优化 数据优化 流量管理 智能报表 3
  • ros发布gps定位信息

    1 可行的主题 Robot Pose EKF节点订阅下面的主题 xff1a odom 2D消息 lt nav msgs Odometry gt imu data 3D消息 lt sensor msgs Imu gt vo 3D消息 lt n
  • tx2备份与恢复

    NVIDIA Jetson是通过Micro USB USB TYPE C接口升级系统 xff08 具体参考不同载板说明 xff09 xff0c 更新前需让Jetson进入Recovery 模式 Recovery 模式下可以进行文件系统更新包
  • debian 163各版本源

    debian8 stable deb http mirrors 163 com debian jessie main non free contrib deb http mirrors 163 com debian jessie updat
  • svn st 状态标识

    svn status 简写 xff1a stat st 显示工作副本中目录与文件的状态 用法 status PATH 未指定参数时 xff0c 只显示本地修改的条目 没有网络访问 常用可选项 xff1a q 只显示本地修改条目的摘要信息 u
  • 单片机蜂鸣器控制程序和驱动电路

    蜂鸣器从结构区分分为压电式蜂鸣器和电磁式蜂鸣器 压电式为压电陶瓷片发音 xff0c 电流比较小一些 xff0c 电磁式蜂鸣器为线圈通电震动发音 xff0c 体积比较小 按照驱动方式分为有源蜂鸣器和无源蜂鸣器 这里的有源和无源不是指电源 xf
  • Linux 下的信号量

    linux下的posix有名信号量的几个要点 博客园 最全面的linux信号量解析 csdn blog Semaphore信号量总结 博客园 sem timedwait csdn blog 一 信号量的概念 信号量的使用主要是用来保护共享资