Linux进程间通信—信号量

article/2025/11/7 6:16:23

一、概述

  进程间通信(interprocess communication,简称 IPC)指两个进程之间的通信。系统中的每一个进程都有各自的地址空间,并且相互独立、隔离,每个进程都处于自己的地址空间中。所以同一个进程的不同模块譬如不同的函数)之间进行通信都是很简单的,譬如使用全局变量等。但是,两个不同的进程之间要进行通信通常是比较难的,因为这两个进程处于不同的地址空间中。
  Linux 内核提供了多种 IPC 机制,其中System V IPC 包括:System V 信号量、System V消息队列、System V 共享内存。这三种通信机制有很多相似之处。
在这里插入图片描述
  信号量是一个计数器,与其它进程间通信方式不大相同,它主要用于控制多个进程间或一个进程内的多个线程间对共享资源的访问,相当于内存中的标志,进程可以根据它判定是否能够访问某些共享资源,同时,进程也可以修改该标志,除了用于共享资源的访问控制外,还可用于进程同步。
  它常作为一种锁机制,防止某进程在访问资源时其它进程也访问该资源,因此,主要作为进程间以及同一个进程内不同线程之间的同步手段。Linux 提供了一组精心设计的信号量接口来对信号量进行操作,它们声明在头文件 sys/sem.h 中。

二、Linux中使用信号量的API

  为了进行临界区保护。信号量一种访问机制,让一个临界区同一时间只有一个线程在访问它,也就是说信号量是用来调协进程对共享资源的访问的。
  信号量是一个特殊的变量,程序对其访问都是原子操作,且只允许对它进行等待(即P(信号变量))和发送(即V(信号变量))信息操作。最简单的信号量是只能取0和1的变量,这也是信号量最常见的一种形式,叫做二进制信号量。而可以取多个正整数的信号量被称为通用信号量。
  由于信号量只能进行两种操作等待和发送信号,即P(sv)和V(sv),他们的行为是这样的:
  P(sv):如果sv的值大于零,就给它减1;如果它的值为零,就挂起该进程的执行
  V(sv):如果有其他进程因等待sv而被挂起,就让它恢复运行,如果没有进程因等待sv而挂起,就给它加1.
  例如:两个进程共享信号量sv,一旦其中一个进程执行了P(sv)操作,它将得到信号量,并可以进入临界区,使sv减1。而第二个进程将被阻止进入临界区,因为当它试图执行P(sv)时,sv为0,它会被挂起以等待第一个进程离开临界区域并执行V(sv)释放信号量,这时第二个进程就可以恢复执行。

2.1、ftok函数

  系统建立IPC通讯 (消息队列、信号量和共享内存) 时必须指定一个ID值。通常情况下,该id值通过ftok函数得到。函数原型:

key_t ftok( const char * fname, int id )

  fname:就是指定的文件名(已经存在的文件名),一般使用当前目录
  id:子序号。虽然是int类型,但是只使用8bits(1-255)。
  返回值:消息队列使用的ID值。

2.2、semget函数

  它的作用是创建一个新信号量或取得一个已有信号量,原型为:

int semget(key_t key, int num_sems, int sem_flags);

  key:信号量关联的键,由ftok函数产生。不相关的进程可以通过它访问一个信号量,它代表程序可能要使用的某个资源,程序对所有信号量的访问都是间接的,程序先通过调用semget函数并提供一个键,再由系统生成一个相应的信号标识符(semget函数的返回值),只有semget函数才直接使用信号量键,所有其他的信号量函数使用由semget函数返回的信号量标识符。如果多个程序使用相同的key值,key将负责协调工作。
  num_sems:指定需要的信号量数目,它的值几乎总是1。
  sem_flags:是一组标志,当想要当信号量不存在时创建一个新的信号量,可以和值IPC_CREAT做按位或操作。设置了IPC_CREAT标志后,即使给出的键是一个已有信号量的键,也不会产生错误。而IPC_CREAT | IPC_EXCL则可以创建一个新的,唯一的信号量,如果信号量已存在,返回一个错误。
  返回值:相应信号标识符(非零),失败返回-1.

2.3、semop函数

  在 Linux 下,PV 操作通过调用semop函数来实现。它的作用是改变信号量的值,原型为:

int semop(int sem_id, struct sembuf *sem_opa, size_t num_sem_ops);

  sem_id:是由semget返回的信号量标识符
  sembuf结构的定义如下:

struct sembuf{short sem_num;//除非使用一组信号量,否则它为0short sem_op;//信号量在一次操作中需要改变的数据,通常是两个数,一个是-1,即P(等待)操作,//一个是+1,即V(发送信号)操作。short sem_flg;//通常为SEM_UNDO,使操作系统跟踪信号,//并在进程没有释放该信号量而终止时,操作系统释放信号量
};

  num_sem_ops:信号操作结构的数量,恒大于或等于1。
  semop函数执行P操作时通常执行以下操作

 struct  sembuf  buf  = { 0, -1, SEM_UNDO};semop ( semid, &buf, 1) 

  semop函数执行V操作时通常执行以下操作

 struct  sembuf  buf  = { 0, 1, SEM_UNDO};semop ( semid, &buf, 1) 

2.4、semctl函数

  该函数用来控制信号量,它与共享内存的shmctl函数和消息队列的msgctl相似,它的原型为:

int semctl(int sem_id, int sem_num, int command, ...);

  sem_id、sem_num两个参数与semget函数中的一样。
  command:通常是下面两个值中的其中一个
    SETVAL:用来把信号量初始化为一个已知的值。p 这个值通过union semun中的val成员设置,其作用是在信号量第一次使用前对它进行设置。
    IPC_RMID:用于删除一个已经无需继续使用的信号量标识符。
  如果有第四个参数,它通常是一个union semum结构,定义如下:

union semun{int val;struct semid_ds *buf;unsigned short *arry;
};

三、例程

#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/sem.h>union semun
{int val;struct semid_ds *buf;unsigned short *arry;
};static int sem_id = 0;
static int set_semvalue()
{//用于初始化信号量,在使用信号量前必须这样做union semun sem_union;sem_union.val = 1;if(semctl(sem_id, 0, SETVAL, sem_union) == -1)return 0;return 1;
}static void del_semvalue()
{//删除信号量union semun sem_union;if(semctl(sem_id, 0, IPC_RMID, sem_union) == -1)fprintf(stderr, "Failed to delete semaphore\n");
}static int semaphore_p()
{//对信号量做减1操作,即等待P(sv)struct sembuf sem_b;sem_b.sem_num = 0;sem_b.sem_op = -1;//P()sem_b.sem_flg = SEM_UNDO;if(semop(sem_id, &sem_b, 1) == -1){fprintf(stderr, "semaphore_p failed\n");return 0;}return 1;
}static int semaphore_v()
{//这是一个释放操作,它使信号量变为可用,即发送信号V(sv)struct sembuf sem_b;sem_b.sem_num = 0;sem_b.sem_op = 1;//V()sem_b.sem_flg = SEM_UNDO;if(semop(sem_id, &sem_b, 1) == -1){fprintf(stderr, "semaphore_v failed\n");return 0;}return 1;
}
int main(int argc, char *argv[])
{char message = 'X';int i = 0;key_t sem_key = Ftok(FILE_PATH, 'a'));//获取系统IPC键值//创建信号量sem_id = semget(sem_key, 1, 0666 | IPC_CREAT);if(argc > 1){//程序第一次被调用,初始化信号量if(!set_semvalue()){fprintf(stderr, "Failed to initialize semaphore\n");exit(EXIT_FAILURE);}//设置要输出到屏幕中的信息,即其参数的第一个字符message = argv[1][0];sleep(2);}for(i = 0; i < 10; ++i){//进入临界区if(!semaphore_p())exit(EXIT_FAILURE);//向屏幕中输出数据printf("%c", message);//清理缓冲区,然后休眠随机时间fflush(stdout);sleep(rand() % 3);//离开临界区前再一次向屏幕输出数据printf("%c", message);fflush(stdout);//离开临界区,休眠随机时间后继续循环if(!semaphore_v())exit(EXIT_FAILURE);sleep(rand() % 2);}sleep(10);printf("\n%d - finished\n", getpid());if(argc > 1){//如果程序是第一次被调用,则在退出前删除信号量sleep(3);del_semvalue();}exit(EXIT_SUCCESS);
}

http://chatgpt.dhexx.cn/article/Uh1mgVdd.shtml

相关文章

Linux操作系统-信号量

信号量也属于一种进程间通信的机制&#xff0c;与其他的进程间通信不同&#xff0c;信号量不是用来传输数据的&#xff0c;而是用来进程间同步与互斥。除此之外&#xff0c;信号量还可以实现线程间的互斥。 信号量是什么&#xff1f; 信号量的本质是一个计数器。 一个信号量…

Linux·信号量全解

目录 信号量 进程间 【无名信号量完成 有血缘关系的进程间 互斥】 知识点2【有名信号量 没有血缘进程互斥】 1、创建一个有名信号量 2、信号量的关闭&#xff1a; 3、信号量文件的删除 4、P操作 sem_wait V操作sem_post 销毁信号量sem_destroy 知识点3【有名信号量 没…

Linux-----信号量

信号量 信号量原理信号量概念信号量函数基于环形队列的生产消费模型空间和数据资源生产者和消费者申请、释放信号量模拟实现基于环形队列的生产者消费者模型 信号量原理 之前我们知道被多个执行流同时访问的公共资源叫做临界资源&#xff0c;而临界资源不保护的话会造成数据不…

Linux信号量

文章目录 POSIX信号量信号量的原理信号量的概念信号量函数 二元信号量模拟实现互斥功能基于环形队列的生产消费模型空间资源和数据资源生产者和消费者申请和释放资源必须遵守的两个规则代码实现信号量保护环形队列的原理 POSIX信号量 信号量的原理 我们将可能会被多个执行流同…

Linux —— 信号量

目录 一、POSIX信号量 1. 什么是信号量 2. 信号量的基本原理 二、与信号量相关的操作 1. 初始化信号量 2. 销毁信号量 3. 等待信号量 4. 发布信号量 三、基于环形队列的生产者消费者模型 1. 空间资源和数据资源 2. 生产者和消费者申请和释放资源 四、模拟实现基于…

Double取值intValue()与doubleValue()之参数缺省

Double调用intValue()是四舍五入向下取整。 调用doubleValue()才是取double真实值。

java.lang.NullPointerException: Attempt to invoke virtual method ‘int java.lang.Integer.intValue()‘

问题 对于PreparedStatement 对象设置参数时&#xff0c; 提示该错误; java.lang.NullPointerException: Attempt to invoke virtual method ‘int java.lang.Integer.intValue()’ 具体问题 2022-09-06 21:28:10.695 11368-11755/com.example.electronicmall E/AndroidRunt…

IntValue()方法 和 ValueOf()方法

intValue() 1.intValue()是java.lang.Number类的方法&#xff0c;Number是一个抽象类。Java中所有的数值类都继承它。也就是说&#xff0c;不单是Integer有intValue方法&#xff0c;Double&#xff0c;Long等都有此方法。 2.此方法的意思是&#xff1a;输出int数据。每个数值类…

Double取值intValue()与doubleValue()

描述一个之前没注意&#xff0c;手误造成的bug。 可以看出&#xff0c;Double调用intValue()结果类似于RoundingMode.DOWN。 调用doubleValue()才是取double真实值。

java中valueof_JAVA中intValue()和ValueOf()什么意思,还有Value什么意思

展开全部 intValue()和ValueOf()是数据类62616964757a686964616fe59b9ee7ad9431333366306538型转化的两个方法。 intValue() 如Integer类型&#xff0c;就会有intValue()方法&#xff0c;意思是说&#xff0c;把Integer类型转化为Int类型。 valueOf() 如String就有valueOf()方法…

IDEA告警:Unnecessary unboxing ‘xxx.intValue()‘

显式编码拆箱已包装的原始数值。在Java5及以上的版本&#xff0c;拆箱是不必要的&#xff0c;可以安全地删除。那么 JDK5 到底做了啥&#xff1f; 自动装箱&#xff08;auto-boxing&#xff09;与自动拆箱&#xff08;auto-unboxing&#xff09; Java语言的基本类型都有包装&…

latex自定义插入空行或者空格

空行有几种方法&#xff1a; 1.~\\ 2.\\[行距] 例如&#xff1a;\\[3pt] 最后&#xff0c;我的选择是&#xff1a; \vspace*{n\baselineskip}空格&#xff1a;

latex中加入空白行的一种方法

在两行文字中间加入“~\\”就可以达到空行的目的了 如图所示&#xff0c;上面是加入“~\\”&#xff0c;下图是得到的结果

latex句首缩进空格

有时候想要再句首加空格&#xff0c;但是会被自动忽略&#xff0c;于是可以使用命令 \hspace*{0.6cm} 例如&#xff1a; $initialize the initial solutions w,precision,max_iters,\\ while (w< precision) and (iters < max_iters):\\\hspace*{0.6cm} grad \gets \…

LaTeX数学公式中的空格

两个quad空格 a \qquad b 两个m的宽度 quad空格 a \quad b 一个m的宽度 大空格 a\ b 1/3m宽度 中等空格 a\;b 2/7m宽度 小空格 a\,b 1/6m宽度 没有空格 ab 紧贴 a\!b 缩进1/6m宽度 \quad、1em、em、m代表当前字体下接近字符‘M’的宽度&#xff08;approximately the …

Latex空格

Latex空格分许多种&#xff0c;分别有&#xff1a; 源代码 \documentclass[UTF8]{ctexart}\begin{document}\begin{tabular}{|l|l|l|l|} \hline 类别 & 用法 & 含义 & 效果\\ \hline 两个quad & a$\backslash$qquad b & 两个m的宽度 & a\qquad b \\ \h…

Latex 公式中空格添加

转自&#xff1a;https://blog.csdn.net/bennyfun79/article/details/15500575 两个quad空格a \qquad b两个m的宽度quad空格a \quad b一个m的宽度大空格a\ b1/3m宽度中等空格a\;b2/7m宽度小空格a\,b1/6m宽度没有空格ab 紧贴a\!b缩进1/6m宽度

latex中的对与错(对号√与叉号×)、空格

转载 LaTeX 对号和错号_Xovee的博客-CSDN博客_latex对号错号 【转载】LaTeX中的空格汇总_AXYZdong的博客-CSDN博客_latex空格符号怎么打出来 空格 对号√ 错号 代码 \documentclass{article} \usepackage[utf8]{inputenc} \usepackage{url} \usepackage{booktabs} \usepackag…