信号量

这篇具有很好参考价值的文章主要介绍了信号量。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

信号量(semaphore)和信号只有一字之差,却是不同的概念,信号量与之前介绍的IPC不同,它是一个计数器,用于实现进程间的互斥于同步

本文参考:

Linux 的信号量_linux 信号量_行孤、的博客-CSDN博客

【Linux】Linux的信号量集_Yngz_Miao的博客-CSDN博客

Linux进程间通信(九)——信号量_linux 信号量_天山老妖的博客-CSDN博客

信号量函数(semget、semop、semctl)及其范例_semget函数_guoping16的博客-CSDN博客

注意,在Linux中,有两类信号量:

  • 第一类是由 semget/semop/semctl API 定义的信号量的 SVR4(System V Release 4)版本。
  • 第二类是由 sem_init/sem_wait/sem_post/interfaces 定义的 POSIX 接口。

它们具有相同的功能,但接口不同本节学习的是第一类信号量

概念

信号量本质上是一个计数器,用于协调多个进程(但不包括父子进程)对共享数据对象的读/写。它不以传输数据为目的,主要是用来保护临界资源(一次仅允许一个进程使用的资源称为临界资源,很多物理设备都属于临界资源,比如打印机,磁带机),保证临界资源在一个时刻只有一个进程独享。

信号量是描述某一种资源是否可用的变量,信号量的值表示当前可用的资源的数量,若信号量的值等于0则意味着目前没有可用的资源

信号量是一个特殊的变量,只允许进程对它进行等待信号(P)和发送信号(V)操作

  • P操作(拿):等待。如果sv大于0,减小sv。如果sv为0,挂起这个进程的执行
  • V操作(放):发送信号。如果有进程被挂起等待sv,使其恢复执行。如果没有进行被挂起等待sv,增加sv

最简单的信号量是只能取0,1的信号量,这也是信号量最常见的一种形式,叫做二值信号量,而可以取多个正整数的信号量称为通用信号量。 

信号量集

就是由多个信号量组成的一个数组。作为一个整体信号量集中的所有信号量使用同一个等待队列。Linux的信号量集为进程请求多个资源创造了条件。Linux规定,当进程的一个操作需要多个共享资源时,如果只成功获得了其中的部分资源,那么这个请求即告失败,进程必须立即释放所有已获得资源,以防止形成死锁。

相关API

Linux下的信号量函数都是在通用的信号量数组(信号量集)上进行操作,而不是在一个单一的二值信号上进行操作。

semget函数

获取或创建信号量组

需要添加的库
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
函数原型
int semget(key_t key, int nsems, int semflg);
函数参数
  • key:IPC键值
  • nsems:信号量集中信号量的个数
  • semflag:取以下值中的一个,若不取0,还要或上新建信号量集的权限
  • 0(取信号量集标识符,若不存在则函数会报错)
  • IPC_CREAT:不存在则创建信号量集
  • IPC_CREAT|IPC_EXCL:不存在则创建信号量集,若存在会报错
  • 返回值:成功返回信号量集ID,失败返回-1

semop函数

对信号量组进行操作,改变信号量的值,也就是对信号量进行P或V操作

需要添加的库
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
函数原型
int semop(int semid, struct sembuf *sops, size_t nsops);
函数参数
  • semid:信号量集ID,semget的返回值
  • sops:一个结构体:
struct sembuf
{
  short sem_num;   // 信号量集的个数,单个信号量设置为0。
  short sem_op;    // 信号量在本次操作中需要改变的数据:-1-等待操作;1-发送操作。
  short sem_flg;   // 把此标志设置为SEM_UNDO,操作系统将跟踪这个信号量。如果设置为IPC_NOWAIT则会直接返回
                   // 如果当前进程退出时没有释放信号量,操作系统将释放信号量,避免资源被死锁。
};
  • nsops:操作信号量的个数,即上一个参数sops结构变量的个数(只有一个就写1)
  • 返回值:成功返回0,失败返回-1
使用举例
封装P操作:
void pGet(int semid)
{
        struct sembuf sops;
        sops.sem_num = 0;
        sops.sem_op = -1;
        sops.sem_flg = SEM_UNDO;

        if(semop(semid,&sops,1) == -1){
                printf("pGet error!\n");
        }else{
                printf("pGet success\n");
        }

}
封装V操作:
void vPut(int semid)
{
        struct sembuf sops;
        sops.sem_num = 0;
        sops.sem_op = 1;
        sops.sem_flg = SEM_UNDO;

        if(semop(semid,&sops,1) == -1){
                printf("vPut error!\n");
        }else{
                printf("vPut success\n");
        }

}

semctl函数

控制信号量(常用于设置信号量的初始值和销毁信号量)

需要添加的库
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
函数原型
int semctl(int semid, int semnum, int cmd, ...);
函数参数
  • semid:信号量集ID,semget的返回值
  • semnum:要操作的目标信号量,第一个就是0,第二个就是1....(只有一个就写0)
  • cmd:对目标信号量采取的命令,取以下值中的一个:

IPC_STAT

IPC_SET

IPC_RMID:销毁信号量,此时没有第四个参数

IPC_INFO

SEM_INFO

SEM_STAT

GETALL

GETNCNT

GETPID

GETVAL

GETZCNT

SETALL

SETVAL:设置信号量的初始值,此时有第四个参数

  • 第四个参数:根据cmd设置的情况决定是否存在这个参数,如果存在,那这个参数的类型就是一个名为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) */
};
使用举例

假设我要操作semid是1234的信号量集中的第一个信号量,将这个信号量的值初始化为1:

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) */
};


int main()
{
    union semun mysemun_1;
    mysemun_1.val = 1; 
    semctl(1234,0,SETVAL,mysemun_1);

    return 0;
}

回顾联合体的知识:和结构体不同,联合体的成员共用存储空间,所以对多个成员的赋值可能会导致覆盖,产生问题,所以此处只需要对val进行赋值,并不用为了防止野指针而对其他成员赋值

实操演示

需求:创建并初始化一个只有一个信号量的信号量集,然后fork一下,之后通过操控信号量让子进程先打印一句话,打印完才允许父进程打印另一句话

sem1.c:

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <stdio.h>
#include <unistd.h>

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) */
};

void pGet(int semid)
{
	struct sembuf sops;
	sops.sem_num = 0;
	sops.sem_op = -1;
	sops.sem_flg = SEM_UNDO;

	if(semop(semid,&sops,1) == -1){
		printf("pGet error!\n");
	}else{
		printf("pGet success\n");
	}
	
}


void vPut(int semid)
{
        struct sembuf sops;
        sops.sem_num = 0;
        sops.sem_op = 1;
        sops.sem_flg = SEM_UNDO;

        if(semop(semid,&sops,1) == -1){
                printf("vPut error!\n");
        }else{
                printf("vPut success\n");
        }

}



int main()
{
	key_t key;
	key = ftok(".",2);
	int semid;
	pid_t pid;

	semid = semget(key, 1, IPC_CREAT|0666);
	if(semid == -1){
		printf("semget error\n");
		return 1;
	}else{
		printf("get semid = %d\n",semid);
	}

	union semun mysemun_1;
	mysemun_1.val = 0; //初始化为无锁的状态
	semctl(semid,0,SETVAL,mysemun_1);

	pid = fork();
	if(pid>0){
		pGet(semid); //尝试获取锁
		printf("this is father\n");
		semctl(semid,0,IPC_RMID); //销毁信号量
	}else if(pid == 0){
		printf("this is son\n");
		vPut(semid); //放入锁
	}else{
		printf("fork error\n");
		return 1;
	}

	return 0;
}

实现效果:

信号量,linux,运维,服务器,系统编程,C语言

由于一开始初始化信号的时候将信号量设置为了0,也就是无锁状态:

而父进程首先是想拿锁,所以必然被阻塞,只有等子进程先打印一句话然后将锁放进去之后,父进程才可以拿到锁并打印自己的那句话。

且此时不会产生僵尸进程,原因是“只有当子进程退出,父进程还在运行且不收集子进程退出状态”时才会产生僵尸进程,而此时父子进程都是打了一句话就都返回退出了。

 文章来源地址https://www.toymoban.com/news/detail-659204.html

到了这里,关于信号量的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【Linux】进程间通信 -- 信号量

    信号量是什么? 本质是一个计数器,通常用来表示公共资源中,资源数量多少的问题 公共资源:能被多个进程同时可以访问的资源 访问没有保护的公共资源:数据不一致问题(比如我想写abc123,但是我123还没有写入,就读取了abc,可能数据分开会导致数据无意义) 为什么要

    2024年02月16日
    浏览(39)
  • linux进程间通信(信号量)

    信号量是一个特殊的变量,程序对其访问都是原子操作,且只允许对它进行等待(即 P(信号变量))和发 送(即 V(信号变量))信息操作。最简单的信号量是只能取 0 和 1 的变量,这也是信号量最常见的一种形式, 叫做二进制信号量。而可以取多个正整数的信号量被称为通用信号

    2024年02月07日
    浏览(29)
  • 【Linux篇】第十七篇——信号量

    前言 POSIX信号量 信号量的概念 信号量的工作原理 信号量函数 二元信号量模拟实现互斥功能 基于环形队列的生产消费模型 空间资源和数据资源 生产者和消费者申请和释放资源 必须遵守的两个规则 代码实现 信号量保护环形队列的原理 将可能被多个执行流同时访问的资源叫

    2024年02月06日
    浏览(37)
  • linux中互斥锁,自旋锁,条件变量,信号量,与freeRTOS中的消息队列,信号量,互斥量,事件的区别

    对于目前主流的RTOS的任务,大部分都属于并发的线程。 因为MCU上的资源每个任务都是共享的,可以认为是单进程多线程模型。 【freertos】003-任务基础知识 在没有操作系统的时候两个应用程序进行消息传递一般使用全局变量的方式,但是如果在使用操作系统的应用中用全局变

    2024年02月11日
    浏览(33)
  • linux(system V标准)信号量

    目录:             1.什么是信号量             2.信号量的本质 1.什么是信号量   2.信号量的本质  什么是临界资源呢?? 凡是倍多个执行流同时访问的资源就是临界资源!!! 我们看一个问题,我们fork()之后创建一个子进程,那么我们的全局变量,是不是我们父

    2024年02月07日
    浏览(33)
  • Linux进程间通信【消息队列、信号量】

    ✨个人主页: 北 海 🎉所属专栏: Linux学习之旅 🎃操作环境: CentOS 7.6 阿里云远程服务器 在 System V 通信标准中,还有一种通信方式: 消息队列 ,以及一种实现互斥的工具: 信号量 ;随着时代的发展,这些陈旧的标准都已经较少使用了,但作为 IPC 中的经典知识,我们可

    2024年02月08日
    浏览(30)
  • 【Linux】多线程 之 POSIX信号量

    信号量又称为 信号灯 本质就是一个计数器,用于描述临界资源数目的 sem: 0 - 1 - 0 若临界资源只有1个,则sem设为1,当要使用临界资源时,sem由1变为0,其他人在想申请,则申请不到挂起排队,等待释放临界资源时 sem由0变为1 ,才可以再申请临界资源 这种信号量称为 二元信号

    2024年02月16日
    浏览(39)
  • 『Linux』第九讲:Linux多线程详解(五)_ 信号量

    「前言」文章是关于Linux多线程方面的知识,上一篇是 Linux多线程详解(四),今天这篇是 Linux多线程详解(五),内容大致是信号量,讲解下面开始! 「归属专栏」Linux系统编程 「主页链接」个人主页 「笔者」枫叶先生(fy) 「枫叶先生有点文青病」「每篇一句」 求其上,

    2024年02月07日
    浏览(42)
  • linux eventfd事件通知 比信号量更好用

      专栏内容:linux下并发编程 个人主页:我的主页 座右铭:天行健,君子以自强不息;地势坤,君子以厚德载物. 目录 前言 概述 原理简介 使用场景 接口说明 头文件 参数说明 代码演示 默认参数 信号量模式 结尾   本专栏主要分享linux下并发编程相关知识,包括多进程,多

    2024年02月08日
    浏览(63)
  • 【linux】进行间通信——共享内存+消息队列+信号量

    进程间通信方式目前我们已经学了匿名管道,命名管道。让两个独立的进程通信,前提是看到同一份资源。匿名管道适用于血缘关系的进程,一个打开写端一个打开读端实现的。命名管道适用于完全独立的进程,打开同一份文件实现的。 接下来我们看看剩下的实现进程间通信

    2024年02月05日
    浏览(39)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包