Linux进程间通讯之信号量

本文深入讲解了信号量机制,包括信号量的概念、作用及其在进程同步互斥中的应用。介绍了信号量的创建、初始化、操作及删除等核心API,并通过一个具体的多进程编程示例,演示了如何使用信号量来实现进程间的同步。

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

信号量

信号量用于进程同步互斥。信号量可以直接理解成计数器,信号量会有初始值>0,每当进程申请使用信号量,通过P操作来对信号量进行-1操作。当计数器(信号量)减到0时说明没有资源了,其他进程要访问就必须在临界区外等待,该进程执行完临界区操作之后,就会执行V操作来对信号量进行+1操作。
临界区:只能被一个进程同时使用(不可多个进程共享),要用到互斥。

(1)创建信号量

semget(key,nsems,semflg):成功返回信号量的id,失败时返回-1,并设置errno值。
key:和信号量所关联的key值,由ftok()函数的返回值得到;
nsems:创建的信号量的个数,传0则代表通过key值获取信号量id;
semflg:IPC_CREAT 和 IPC_EXCL,传0代表不做创建的动作;

(2)信号量的初始化

semctl(semid,semnum,cmd,…):初始化信号量,成功返回0,失败返回-1,并设置errno值。
semid:信号量id,由semget()函数的返回值得到。
semnum:要初始化第几个信号量,信号量编号从0开始;
cmd:SETVAL;
参数4:union semnum类型的共用体,填共用体中的成员val,在填之前要对其赋值。联合体中的val 是信号量的初始值。

(3)信号量操作:

semop(semid,struct sembuf *sops,nsops):成功返回0,失败返回-1
semid:信号量id,由semget()函数的返回值得到;

struct sembuf *sops
{
sem_num;//你想操作的信号量的下标
sem_op;//值给1或-1,代表V和P操作
sem_flg;//信号量选项,IPC_NOWAIT(非阻塞)等等,没有选项填0即可
}

nsops:要同时操作几个信号量

(4)删除信号量

semctl(semid,0,IPC_RMID);

例、用多进程实现打印出AABBAABB…的字符串。
sem.h头文件:封装信号量的相关操作

#ifndef SEM_H__
#define SEM_H__

int sem_create(int semnum);
int sem_get();
int sem_init(int semid,int index);
int sem_p(int semid,int index,int nums);
int sem_v(int semid,int index,int nums);
int sem_destroy(int semid,int index);


#endif

sem.c程序:对信号量的操作写自定义函数

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

#include <sem.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) */
};


static int sem_create_comon(int semnum,int ctl)
{
	key_t key;
	int semid;
	key = ftok("sem.c",'a');
	if(key < 0)
	{
		perror("ftok()");
		return -1;
	}
	
	semid = semget(key,semnum,ctl);
	if(semid < 0)
	{
		perror("semget()");
		return -2;
	}


	return semid;
}


int sem_create(int semnum)
{
	return sem_create_comon(semnum,IPC_CREAT|0666);
}

int sem_get()
{
	return sem_create_comon(0,0);
}

int sem_init(int semid,int index)
{
	union semun semn;
	semn.val = 1;
	if(semctl(semid,index,SETVAL,semn) < 0)
	{
		perror("semctl()");
		return -1;
	}

	return 0;
}

static int sem_vp(int semid,int index,int nums,int vp)
{
	struct sembuf sops;
	sops.sem_num = index;
	sops.sem_op = vp;
	sops.sem_flg = 0;
	if(semop(semid,&sops,nums) < 0)
	{
		perror("semop()");
		return -1;
	}

	return 0;
}

int sem_p(int semid,int index,int nums)
{
	return sem_vp(semid,index,nums,-1);
}

int sem_v(int semid,int index,int nums)
{
	return sem_vp(semid,index,nums,1);
}

int sem_destroy(int semid,int index)
{
	return semctl(semid,index,IPC_RMID);
}


print.c程序:打印出AABBAABB…的序列

#include <stdio.h>
#include <stdlib.h>

#include <sem.h>

int main(void)
{
	pid_t pid;
	int semid;
	semid = sem_create(1);
	sem_init(semid,0);

	if( (pid = fork()) < 0)
	{
		perror("fork()");
		exit(-1);
	}

	if(pid ==  0)
	{
		semid = sem_get();

		while(1)
		{
			sem_p(semid,0,1);
			printf("B");
			fflush(NULL);
			sleep(1);
			printf("B");
			fflush(NULL);
			sem_v(semid,0,1);
		}

		exit(0);
	}
	
	while(1)
	{
		sem_p(semid,0,1);
		printf("A");
		fflush(NULL);
		sleep(1);
		printf("A");
		fflush(NULL);
		sem_v(semid,0,1);
	}
	
	wait(NULL);
	sem_destory(semid,0);
	exit(0);
}

Makefile文件:

CFLAGS+=-I./

main:sem.c print.c
	$(CC) -o $@ $^ $(CFLAGS)

对于信号量这个知识点,博主没有理解透彻,恳请高手指点!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值