Linux下信号量使用总结

目录

1.Linux下信号量简介

2.POSIX信号量

2.1 无名信号量

2.2 有名信号量

3.System V信号量


1.Linux下信号量简介

       信号量是解决进程之间的同步与互斥的IPC机制,互斥与同步关系存在的症结在于临界资源。
临界资源是在同一个时刻只容许有限个(一般只有一个)进程可以访问(读)或更改(写)的资源,
临界资源包括:硬件资源(处理器、内存、存储器以及其他外围设备等)和软件资源(共享代码段、共享结构和变量等)。
信号量是描述某一种资源是否可用的变量,信号量的值表示当前可用的资源的数目linux查看信号量命令,若信号量的值等于0则意味着目前没有可用的资源。
对信号量进行的两个原子操作(PV操作)
P操作:等待。假如sv小于0,减少sv。假如sv为0,挂起这个进程的执行。
V操作:发送信号。假如有进程被挂起等待sv,使其恢复执行。假如没有进行被挂起等待sv,降低sv。

Linux系统承继了Unix系统的两种信号量:
(1)内核信号量,由内核控制路径使用
(2)用户态进程使用的信号量,分为POSIX信号量和System V信号量。
(3)POSIX信号量又分为有名信号量和无名信号量,有名信号量,其值保存在文件中,可用于线程、进程间的同步;无名信号量linux deepin,其值保存在显存中。

POSIX信号量与System V信号量的区别如下:
(1)POSIX信号量是个非负整数,常用于线程间同步。System V信号量是一个或多个信号量的集合,是一个信号量结构体,信号量是它的一部份,常用于进程间同步。
(2)POSIX信号量的引用头文件是,而System V信号量的引用头文件是。
(3)System V信号量是复杂的,而POSIX信号量是简单的。


2.POSIX信号量

头文件
/usr/include/semaphore.h
API
(1)int sem_init (sem_t *__sem, int __pshared, unsigned int __value);
(2)int sem_destroy (sem_t *__sem);
(3)sem_t *sem_open (const char *__name, int __oflag, ...);
(4)int sem_close (sem_t *__sem);
(5)int sem_unlink (const char *__name);
(6)int sem_wait (sem_t *__sem);
(7)int sem_timedwait (sem_t *__restrict __sem,
              const struct timespec *__restrict __abstime);
(8)int sem_trywait (sem_t *__sem);
(9)int sem_post (sem_t *__sem);
(10)int sem_getvalue (sem_t *__restrict __sem, int *__restrict __sval);

2.1 无名信号量

用途:线程,亲缘关系进程同步
常见用法是即将保护的变量放到sem_wait和sem_post中间所产生的临界区内。

常见函数说明:
/*
功能:初始化信号量
pshared==0用于同一个进程的多线程的同步。
pshared>0用于多个相关进程间的同步(由fork产生的亲缘进程)
*/
int sem_init(sem_t *sem, int pshared, unsigned int value);

/*
用途:销毁信号量
*/
int sem_destroy(sem_t *sem);

/*
用途:获取信号量当前值
若有1个或更多的线程或进程调用sem_wait阻塞在该信号量上,该函数返回两种值:
1)返回0
2)返回阻塞在该信号量上的进程或线程数量
*/
int sem_getvalue(sem_t *sem, int *sval);

/*
用途:相当于P操作,即申请资源。阻塞函数 
若sem>0,这么它减1并立刻返回。
若sem==0,则睡眠直至sem>0,此时立刻减1,之后返回。
*/
int sem_wait(sem_t *sem); //阻塞函数 
int sem_trywait(sem_t *__sem);

/*
用途:相当于P操作,即申请资源。非阻塞的函数 
若sem>0,这么它减1并立刻返回。
若sem==0,不是睡眠,而是返回一个错误EAGAIN。
*/
int sem_trywait(sem_t *sem);  


/*
用途:相当于V操作,释放资源
把指定的信号量sem的值加1;
呼醒正在等待该信号量的任意线程。
*/
int sem_post(sem_t *sem);

测试程序1:使用信号量实现多线程同步(线程执行次序随机)

#include <stdio.h>
#include <semaphore.h>
#include <pthread.h>

// 被保护的全局变量
int number; 

//信号量
sem_t semid;

void* thread_one(void *arg)
{
    sem_wait(&semid);
    printf("thread_one have the semaphoren\n");
    number++;
    printf("number = %d\n",number);
    sem_post(&semid);
}

void* thread_two(void *arg)
{
    sem_wait(&semid);
    printf("thread_two have the semaphore \n");
    number--;
    printf("number = %d\n",number);
    sem_post(&semid);
}

int main(int argc, char *argv[])
{
    number = 1;
    pthread_t tid1, tid2;
    sem_init(&semid, 0, 1);
    pthread_create(&tid1,NULL,thread_one, NULL);
    pthread_create(&tid2,NULL,thread_two, NULL);
    pthread_join(tid1,NULL);
    pthread_join(tid2,NULL);
    printf("main running...\n");
    return 0;
}
编译执行:
g++ test.cpp -lpthread
./a.out 

测试程序2:使用信号量实现多线程同步(线程执行次序指定)

#include <stdio.h>
#include <semaphore.h>
#include <pthread.h>

// 被保护的全局变量
int number; 
sem_t semid1, semid2;
void* thread_one(void *arg)
{
    sem_wait(&semid1);
    printf("thread_one have the semaphore\n");
    number++;
    printf("number = %d\n",number);
    sem_post(&semid1);
}

void* thread_two(void *arg)
{
    sem_wait(&semid2);
    printf("thread_two have the semaphore \n");
    number--;
    printf("number = %d\n",number);
    sem_post(&semid2);
}

int main(int argc, char *argv[])
{
    number = 1;
    pthread_t tid1, tid2;
    sem_init(&semid1, 0, 1);
    sem_init(&semid2, 0, 0);
    pthread_create(&tid1,NULL,thread_one, NULL);
    pthread_create(&tid2,NULL,thread_two, NULL);
    pthread_join(tid1,NULL);
    pthread_join(tid2,NULL);
    printf("main running...\n");
    sem_destroy(&semid1);
    sem_destroy(&semid2);
    return 0;
}

测试程序3:无名信号量在亲缘进程间的同步


#include <stdio.h>
#include <semaphore.h>
#include <pthread.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
 #include <stdlib.h>

int main(int argc, char **argv)
{
    int fd, i, nloop=10, zero=0, *ptr;
    sem_t mutex;
    //open a file and map it into memory
    fd = open("log.txt",O_RDWR|O_CREAT,S_IRWXU);
    write(fd,&zero,sizeof(int));
    ptr = (int*)mmap( NULL, sizeof(int), PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
    close(fd);
    /* create, initialize semaphore */
    if( sem_init(&mutex,1,1) < 0) //
    {
        perror("semaphore initilization");
        exit(0);
    }
    if (fork() == 0)
    { /* child process*/
        sem_wait(&mutex);
        for (i = 0; i < nloop; i++)
        {
            printf("child: %d\n", (*ptr)++);
        }
        sem_post(&mutex);
        exit(0);
    }
    /* back to parent process */
    sem_wait(&mutex);
    for (i = 0; i < nloop; i++)
    {
        printf("parent: %d\n", (*ptr)++);
    }
    sem_post(&mutex);
    exit(0);
}

编译执行:
g++ test.cpp -lpthread
./a.out 

 

2.2 有名信号量

用途:线程,亲缘关系进程,无亲缘关系进程同步
有名信号量的是把信号量的值保存在文件中

/*
用途:打开信号量文件
name是文件的路径名,在linux中sem都是创建在/dev/shm目录下。
name可以写成“/mysem”或“mysem”,创建的文件都是“/dev/shm/sem.mysem”,不要写路径。
oflag有O_CREAT或O_CREAT|EXCL两个取值;
mode控制新的信号量的访问权限;
value指定信号量的初始化值。
使用时包含头文件:
#include <fcntl.h>           /* For O_* constants */
#include <sys/stat.h>        /* For mode constants */
#include <semaphore.h>
*/
sem_t *sem_open(const char *name, int oflag, mode_t mode , int value);

其他函数同匿名信号量。


3.System V信号量

        System V信号量是SYSTEMVIPC的组成部份,System V信号量在内核中维护,其中包括二值信号量、计数信号量、计数信号量集。
系统中记录信号量的数据结构(structipc_idssem_ids)坐落内核中linux查看信号量命令,系统中的所有信号量都可以在结构sem_ids中找到访问入口。
struct semid_ds {
    struct ipc_permsem_perm ;
    struct sem* sem_base ; //信号数组指针
    ushort sem_nsem ; //此集中信号个数
    time_t sem_otime ; //最后一次semop时间
    time_t sem_ctime ; //最后一次创建时间
};
常见api说明:
(1)semget
/*
功能:创建信号量或获得在系统已存在的信号量,不同进程使用同一个信号量通配符来获得同一个信号量
返回值是一个称为信号量标示符的整数,semop和semctl函数将使用它。
key:所创建或打开信号量集的通配符。须要是惟一的非零整数。
nsem:创建的信号量集中的信号量的个数,该参数只在创建信号量集时有效。通常取值为1.
oflag:调用函数的操作类型,也可用于设置信号量集的访问权限,SEM_R(read)和SEM_A(alter),也可以是IPC_CREAT或IPC_EXCL
*/
int semget (key_t key, int nsem, int oflag);


(2)semctl
/*
功能:初始化信号量
使用semctl()函数的SETVAL操作
当使用二维信号量时,一般将信号量初始化为1
sem_id是由semget返回的信号量标示符。
sem_num是信号量集中的某一个资源
cmd:表示即将采取的动作。最常用的两个值如下:
SETVAL:拿来把信号量初始化为一个已知的值。这个值通过unionsemun中的val成员设置。其作用是在信号量第一次使用之前对它进行设置。
IPC_RMID:用于删掉一个无需继续使用的信号量标志符。
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 */
};
*/
int semctl(int semid, int semnum, int cmd, ...);

3、semop
/*
功能:进行信号量的PV操作
实现进程之间的同步和互斥的核心部分
参数semid是一个通过semget函数返回的一个信号量标示符
参数opsptr是一个表针,指向一个信号量操作字段
参数nops标注了参数semoparray所指向链表中的元素个数
struct sembuf{  
    //除非使用一组信号量,否则它为0  
    short sem_num;
    //信号量在一次操作中需要改变的数据,通常是两个数,一个是-1,即P(等待)操作,
    // 一个是+1,即V(发送信号)操作。  
    short sem_op;
    short sem_flg;//通常为SEM_UNDO,使操作系统跟踪信号,  
    //并在进程没有释放该信号量而终止时,操作系统释放信号量  
};
*/
int semop (int semid, struct sembuf * opsptr, size_t nops) ;

相关命令:

(1)查看信号量

ipcs -s 

(2)删除信号量组

ipcrm sem

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/27565.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【数据结构与算法】03 队列(顺序队列--循环队列--优先级队列--链队列)

一、概念1.1 队列的基本概念1.2 队列的顺序存储结构1.21 顺序队列&#xff08;静态队列&#xff09;1.22 循环队列1.23 优先级队列 1.3 队列的链式存储结构 二、C语言实现2.1 顺序存储2.11 顺序队列2.12 循环队列2.13 优先级队列 2.2 链式存储 一、概念 1.1 队列的基本概念 队…

Linux内核中断和Linux内核定时器

目录 Linux内核中断 Linux内核定时器 Linux内核中断 int request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags,const char *name, void *dev) 功能&#xff1a;注册中断 参数&#xff1a; irq : 软中断号 gpio的软中断号 软中断号 gpio_to_i…

【PCB专题】案例:绕等长怎么直接以颜色区分看出是否绕好

PCB上对于时序的处理,在板卡上实际我们是通过绕等长的手段。做为一个合格的Layout工程师,等长的处理是不可或缺的技能。 一般来说,在绕等长的时候我们可以使用Delay Tune命令来改变走线的长度,然后通过规则管理器中分析看看哪根线长哪根线短。 但是在实际工作中,很可能绕着…

Android应用程序进程的启动过程

Android应用程序进程的启动过程 导语 到这篇文章为止&#xff0c;我们已经简要地了解过了Android系统的启动流程了&#xff0c;其中比较重要的内容有Zygote进程的启动和SystemService以及Launcher的启动&#xff0c;接下来我们将要学习的是Android应用程序的启动过程&#xff…

华为OD机试真题 JavaScript 实现【最多几个直角三角形】【2023Q1 100分】

一、题目描述 有 N 条线段&#xff0c;长度分别为 a[1]-a[n]。 现要求你计算这 N 条线段最多可以组合成几个直角三角形&#xff0c;每条线段只能使用一次&#xff0c;每个三角形包含三条线段。 二、输入描述 第一行输入一个正整数 T (1< T< 100) &#xff0c;表示有…

2023蓝桥杯大学A组C++决赛游记+个人题解

Day0 发烧了一晚上没睡着&#xff0c;感觉鼻子被打火机烧烤一样难受&#xff0c;心情烦躁 早上6点起来吃了个早饭&#xff0c;思考能力完全丧失了&#xff0c;开始看此花亭奇谭 看了六集&#xff0c;准备复习数据结构考试&#xff0c;然后秒睡 一睁眼就是下午2点了 挂了个…

springboot项目外卖管理 day05-新增与删除套餐

文章目录 一、新增菜品1.1、需求分析1.2、数据模型setmealsetmeal_dish 1.3、代码开发-梳理交互过程1.3.1、下拉框展示1.3.2、菜品窗口展示1.3.3、新增套餐 2、套餐分页查询 一、新增菜品 1.1、需求分析 套餐就是菜品的集合。 后台系统中可以管理套餐信息&#xff0c;通过新…

一文打通:从字节码指令的角度解读前置后置自增自减(加加++减减--)

文章目录 1.前置了解的知识1.1 栈这种数据结构1.2 局部变量表和操作数栈1.3 三个字节码指令 2.单独使用后置与前置2.1 后置字节码指令2.2 前置字节码指令2.3 总结 3.需要返回值的情况下使用后置与前置3.1 后置字节码指令3.2 前置字节码指令3.3 总结3.4 练习&#x1f340; 练习一…

了解ASEMI代理英飞凌TLE6208-6G其功能和应用的综合指南

编辑-Z TLE6208-6G是一款高度集成、通用且高效的汽车半桥驱动器&#xff0c;由英飞凌设计。这种功能强大的设备专门设计用于满足汽车应用的苛刻要求&#xff0c;如控制直流电机、螺线管和电阻负载。在本文中&#xff0c;我们将深入研究TLE6208-6G的功能、优点和应用&#xff0…

实现表白墙

我们已经学习了Http以及Servlet类的相关知识 今天我们来实操一下,实现一个简单的既有前端又有后端的网站–表白墙 之前在学习前端的时候已经写过了表白墙的前端代码,存在两个问题 1.页面重启,数据丢失 2.数据只是在本地的,别人看不见 那么这样的问题我们要咋样解决呢? 引入…

(七)CSharp-CSharp图解教程版-事件

一、发布者和订阅者 发布者/订阅者模式&#xff08;publish/subscriber pattern&#xff09;&#xff1a; 很多程序都有一个共同的需求&#xff0c;即当一个特定的程序事件发生时&#xff0c;程序的其他部分可以得到该事件已经发生的通知。 发布者&#xff1a; 发布者类定义…

Excel函数VLOOKUP常用方法

一、基础用法 1、精确匹配 公式&#xff1a;VLOOKUP(待匹配值&#xff0c;查找范围&#xff0c;范围列数&#xff0c;查找方式) 定义好要输出表的表头和第一列&#xff0c;第一列即为要查找和匹配的父内容&#xff0c;在第二列输入公式&#xff0c;被查找表中一定也要将待查…

基于SPAD / SiPM技术的激光雷达方案

激光雷达(LiDAR)是一种测距技术&#xff0c;近年来越来越多地用于汽车先进驾驶辅助系统(ADAS)、手势识别和3D映射等应用。尤其在汽车领域&#xff0c;随着传感器融合的趋势&#xff0c;LiDAR结合成像、超声波、毫米波雷达&#xff0c;互为补足&#xff0c;为汽车提供全方位感知…

【力扣刷题 | 第六天】

目录 前言&#xff1a; 344. 反转字符串 - 力扣&#xff08;LeetCode&#xff09; 541. 反转字符串 II - 力扣&#xff08;LeetCode&#xff09; 今天我们进入字符串章节的刷题旅程&#xff0c;希望各位小伙伴可以和我一起坚持下去&#xff0c;一起征服力扣&#xff01; 前言…

前端前端学习不断

卷吧卷吧...&#xff0c;这东西什么时候是个头啊……

半导体器件基础(期末模电速成)

目录 1、半导体分类 2、PN结 3、二极管 4、稳压二极管 5、三极管 6、场效应管 1、半导体分类 2、PN结 3、二极管 伏安特性&#xff1a; 我们第七版模电书上给的正向导通压降分别约为0.7和0.2V&#xff0c;且硅的单向导电性更好 如何确定二极管状态&#xff1f; 阳极电压…

怎么快速掌握Python爬虫技术?

Python总的来说是一门比较容易入门的编程语言&#xff0c;因为它的语法简洁易懂&#xff0c;而且有很多优秀的教程和资源可供学习。相比其他编程语言&#xff0c;Python 的学习曲线较为平缓&#xff0c;初学者可以很快上手&#xff0c;但要想深入掌握 Python&#xff0c;还需要…

6款AI绘画生成器,让你的创作更有灵感

人工智能绘画听起来很高深&#xff0c;其原理是通过集成文本、图片和其他大数据数据来生成信息库&#xff0c;在输入文本描述的要求后&#xff0c;可以找到相应的视觉元素&#xff0c;然后拼凑起来生成符合文本描述的图片。 本文介绍非常好用的6款AI绘画生成工具 1.即时 AI 绘…

location.href 和 document.URL 与 document.documentURI

location.href 和 document.URL 与 document.documentURI 相同点 获取到的值相同 不同点 location.hrefurl可以赋值, 效果类似location.assign(url) , 可以后退 document.URL 与 document.documentURI 是只读的, 赋值无效 location.href locationwindow.location true lo…

HTTP编码杂谈

一 HTTP编码杂谈 ① 知识铺垫 1) 编码的英文叫encode --> 常见HTTP URL编码、Base64编码等目的&#xff1a; 转变为二进制的stream(字节流),便于网络传输备注&#xff1a; 一般都是基于utf-8编码2) 解码叫decode3) 乱码的根源&#xff1a; 编码和解码的方式不一致4) url…