linux 之 system-v 信号量

本文介绍信号量的基本概念及其在操作系统中的应用。信号量是一种用于保护共享资源的机制,通过计数器记录可访问次数,实现互斥与同步。文章详细解释了信号量的创建、初始化、操作及销毁过程,并提供了一个简单的C语言示例程序。

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

本质

计数器,记录可以访问共享资源的次数。

作用

保护共享资源(硬件设备,文件,共享内存等等)

  • 互斥(一个时刻,只能有一个进程来访问共享资源)
  • 同步(互斥的基础上,加上了访问的顺序规定)

信号量大于0,则可以访问;信号量等于0,则不可以访问。
访问时会将信号量减1,访问完成会将信号量加1。(加1减1可由用户控制),信号量减小到0之后,进程就不能去访问共享资源了。

信号量用法

  • 定义一个唯一的key(ftok)
  • 构造一个信号量(semget)
  • 初始化信号量(semctl SETBA)
  • 对信号量进行 p / v 操作(就是加减操作,semop)
  • 删除信号量(semctl RMID)

semget函数

  • 功能:获取 / 创建信号量的 id
  • 函数原型:
int semget(key_t key,int nsems,int semflg);
  • 参数:

    • key:信号量键值
    • nsems:信号量数量
    • semflg:
      IPC_CREAT:信号量不存在则创建
      mode:信号量的权限,rwx
  • 返回值:
    成功:信号量 id
    失败:-1

semctl函数

  • 功能:获取 or 设置信号量的相关属性
  • 函数原型:
int semctl(int semid,int semnum,int cmd,union semun arg);
  • 参数:

    • semid:信号量 id
    • semnum:信号量编号
    • cmd:
      IPC_STAT:获取信号量的属性信息
      IPC_SET:设置信号量的属性
      IPC_RMID:删除信号量
      IPC_SETVAL:设置信号量的值(特有)
  • arg:

union semun
{
	int val;
	struct semid_ds *buf;
}
  • 返回值:

    • 成功:由 cmd 类型决定,上面列出的四种成功都是返回0。
    • 失败:-1

semop函数

  • 功能:对信号量进行加减操作
  • 函数原型:
int semop(int semid,struct sembuf *sops,size_t nsops);
  • 参数:

    • semid:信号量id
    • sops:信号量操作结构体数组
struct sembuf
{
	short sem_num;//信号量编号
	short sem_op;//信号量 p / v 操作
	short sem_flg;//信号量行为,一般设置为SEM_UNDO,自动释放信号量
};
    • nsops:信号量数量

  • 返回值:
    • 成功:0
    • 失败:-1

测试代码:

test.c

#include<sys/ipc.h>
#include<unistd.h>
#include<stdlib.h>
#include<stdio.h>
#include<string.h>
#include<sys/sem.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<errno.h>

union semun
{
	int val;
	struct semid_ds *buf;
};

//初始化信号量
int init_sem(int sem_id,int init_value)
{
	union semun sem_union;
	sem_union.val = init_value;
	if(semctl(sem_id,0,SETVAL,sem_union) == -1)
	{
		printf("Initialize semaphore.\n");
		return -1;
	}
	return 0;
}

//删除信号量
int del_sem(int sem_id)
{
	union semun sem_union;
	if(semctl(sem_id,0,IPC_RMID,sem_union) == -1)
	{
		perror("Delete semahore.\n");
		return -1;
	}
}

// p 操作
int sem_p(int sem_id)
{
	struct sembuf sops;
	sops.sem_num = 0;//单个信号量的编号应该为0
	sops.sem_op = -1;//表示 p 操作,减操作
	sops.sem_flg = SEM_UNDO;//到最后系统会自动释放系统中残留的信号量
	if(semop(sem_id,&sops,1) == -1)
	{
		perror("P operation.\n");
		return -1;
	}
	return 0;
}

// v 操作
int sem_v(int sem_id)
{
	struct sembuf sops;
	sops.sem_num = 0;//单个信号量的编号为 0 
	sops.sem_op = 1;//表示 v 操作
	sops.sem_flg = SEM_UNDO;//系统自动释放残留的信号量
	if(semop(sem_id,&sops,1) == -1)
	{
		perror("V operation.\n");
		return -1;
	}
	return 0;
}

#define DELAY_TIME	3

int main(void)
{
	pid_t result;
	int sem_id;
	//创建一个信号量
	sem_id = semget((key_t)6666, 1, 0666 | IPC_CREAT);
	//信号量的id,和信号量初始化的值为0
	init_sem(sem_id, 0);
	//调用 fork() 函数
	result = fork();
	if(-1 == result)
	{
		perror("Fail fork.\n");
	}
	else if(0 == result)//子进程
	{
		printf("Child process will wait for some seconds ...\n");
		sleep(DELAY_TIME);
		printf("The child process is running ...\n");
		//给信号量的值加1
		sem_v(sem_id);
	}
	else//父进程
	{
		//若信号量的值已经为0,那么这个减1的操作将阻塞于此
		//从而保证子进程运行在父进程的前面
		sem_p(sem_id);
		printf("The father process is running ...\n");
		sem_v(sem_id);
		del_sem(sem_id);
	}
	exit(0);
}

编译与运行:

jl@jl-virtual-machine:~/test/11_4$ gcc test.c 
jl@jl-virtual-machine:~/test/11_4$ 
jl@jl-virtual-machine:~/test/11_4$ 
jl@jl-virtual-machine:~/test/11_4$ 
jl@jl-virtual-machine:~/test/11_4$ ./a.out 
Child process will wait for some seconds ...
The child process is running ...
The father process is running ...
jl@jl-virtual-machine:~/test/11_4$ 
jl@jl-virtual-machine:~/test/11_4$ 
jl@jl-virtual-machine:~/test/11_4$ 

可以看到,父进程总是在子进程后面运行。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值