c语言基础(十五)

本文深入讲解了信号量机制的基本概念及工作原理,并提供了详细的Linux环境下信号量应用实例,包括信号量的创建、初始化、PV操作及删除等关键步骤。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

信号量

信号量:解决进程之间通信时,资源的一个同步和互斥。
对信号量由两个操作(PV操作),这两个操作都是原子的。
原子操作:要不执行,要不执行,一旦执行肯定可以完成。
信号量对于某种资源(buffer),是一个非负的整型值。信号量>0时,就表示资源可以用,
你就可以进行原子操作。信号量==0,这里就不能操作这个资源,就会阻塞在这句代码处。
P操作:(>0),就是操作资源,占用一个资源后,就对这个信号量(-1),操作完成后,必须信号量(+1)。
V操作:(==0)没有进程排队等待,释放一个资源(+1);如果有这个信号量, 激活某一个进程。
信号量的工作流程:
S代表信号量;R代表资源
1、init(S),初始化信号量(创建一个信号量: 1(一般做法))
非临界区:
P(S)
临界区:资源R,允许有限个进程操作该资源(1个)
V(S)
非临界区
简单的信号量:0和1;这个信号量叫做二维信号量
说一下:linux二维信号量的应用步骤:
1、创建一个信号量或者获取已经在系统中存在的信号量,获取信号量semget,
不同的进程可以通过同一键值获得同一个信号量。
2、初始化信号量,函数semctl。
3、对信号量进行PV操作,函数semop.
4、删除系统中的信号量,函数semctl。PS:不要重复删除(已删除的)信号量。
介绍以下:
1、int semget(key_t key, int nsems, int semflg);
key:键值,用法,参考之前。
nsems:创建信号量的数目,通常写1。
semflag:参考open,注意:可以或上权限的。
2、int semctl(int semid, int semnum, int cmd, …);
semid:第1步获得的信号量标识符数字。
semnum:用到信号量(集合)时,才会使用。单个信号就写0。
cmd:
IPC_RMID表示:删除系统信号量。成功返回0,失败-1.
IPC_STAT表示:获得信号量(信号量集合)的 struct semid_ds,存在第4个参数所指向的buf中,
这个结构体就是描述系统信号量的数据结构。成功返回0,失败-1.
SETVAL表示:信号量的值(初始值)在第四个参数arg的内部元素(val)设置。成功返回0,失败-1.
第四个参数:union semun arg,有时候可能系统不认识这个共用体,你就得自己定义。
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) */
};
3、int semop(int semid, struct sembuf *sops, unsigned nsops);
第二个参数的结构体类型:
//信号量编号,使用单个信号量时,值写为0
unsigned short sem_num; /* semaphore number */
// 写为1,就表示V操作,写-1,就表示P操作。
short sem_op; /* semaphore operation */
// SEM_UNDO 表示系统退出后,如果有未被释放的信号量,就自动把这个信号量释放。
short sem_flg; /* operation flags */

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

union semun
 {
      int  val; 
      struct semid_ds *buf;
      unsigned short  *array;
      struct seminfo  *__buf;
 };
//初始化信号量
int init_sem(int semid, int init_val)
{
        union semun arg;
        //信号量的初始值
        arg.val = init_val;
        if (semctl(semid, 0, SETVAL, arg) == -1)
        {
                perror("semctl初始化");
                exit(-1);
        }

        return 0;
}
//信号量P操作
int sem_p(int semid)
{
        struct sembuf sem_b;
        sem_b.sem_num = 0;
        sem_b.sem_op = -1;
        sem_b.sem_flg = SEM_UNDO;
        //最后一个参数写成1,表示只操作一个结构体
        if (semop(semid, &sem_b, 1) == -1)
        {
                perror("P操作");
                exit(-1);
        }

        return 0;       
}
//信号量V操作
int sem_v(int semid)
{
        struct sembuf sem_b;
        sem_b.sem_num = 0;
        sem_b.sem_op = 1;
        sem_b.sem_flg = SEM_UNDO;
        //最后一个参数写成1,表示只操作一个结构体
        if (semop(semid, &sem_b, 1) == -1)
        {
                perror("V操作");
                exit(-1);
        }

        return 0;       
}
//删除信号量
int del_sem(int semid)
{
        union semun arg;
        if (semctl(semid, 0, IPC_RMID, arg) == -1)
        {
                perror("semctl删除");
                exit(-1);
        }

        return 0;
}
/*
        控制父子进程的执行顺序
*/

#define DELAY 3
int main(void)
{
        pid_t pid;

        int semid = 0;
        //创建一个信号量
        semid = semget(ftok(".", 'A'), 1, 0666 | IPC_CREAT);
        if (semid < 0)
        {
                perror("semget");
                exit(0);
        }
        //初始化信号量
        init_sem(semid, 0);

        //创建进程
        pid = fork();

        if (pid < 0)
        {
                perror("fork");
                exit(0);
        }
        if (pid == 0)
        {
                //子进程
                printf("子进程开始......\n");
                sleep(DELAY);
                printf("子进程的pid = %d.\n", getpid());
                //释放一个资源
                sem_v(semid);
        }
        if (pid > 0)
        {
                // 父进程
                sem_p(semid);
                printf("父进程的pid = %d.\n", getpid());
                sem_v(semid);
                //删除信号量
                del_sem(semid);
        }

        return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值