进程间的通信之信号量

信号量就是具有原子性的计数器,就相当于一把锁,在每个进程要访问临界资源时,必须要向信号量拿个锁”,它才能进去临界资源这个“房间”,并锁上门,不让其他进程进来,此时信号量执行P()操作,锁的数目减少了一个,所以计数器减1,;当它访问完成时,它出来,将锁还给信号量,执行V()操作,计数器加1;

1.semget函数:创建信号量

该函数用来创建一个新信号量,其定义为:int semget(key_t key, int num, int sem_flags)
Key是整数值,程序对所有信号量的访问都是间接的,先提供一个键,再由系统生成一个信号量标识符。num_sem参数指定需要的信号量数目,一般取1;sem_flags参数是一组标志。
semget函数在成功时返回一个正数,也就是其他信号量函数用到的信号量标识符,失败时返回-1.

2.semop函数:改变信号量的值

定义为:int semop(int sem_id, struct sembuf *sem_ops, size_t num_sem_ops)
sem_id 表示信号量标识符,semops指向一个结构数组的指针,每个数组元素至少包含以下几个成员:
struct sembuf {
short sem_num; //信号量编号,一般取0
short sem_op;//信号量需要改变的值,-1/+1
short sem_flg;//设置为SEM_UNDO
}

3.semctl函数:控制信号量的信息

定义为:int semctl(int sem_id, int sem_num, int command,…)
sem_id:表示信号量标识符,sem_num表示信号量编号一般取0,command参数是将要采取的行动,如:SETVAL:用来把信号量初始化为一个已知的值,作用就是在信号量第一次使用前对它进行设置。IPC_RMID用于删除一个不再继续使用的信号量标识符。如果还有第四个参数,它是一个union semun结构(该联合结构可能需自己定义,可通过查阅semctl的手册查看是否给出了该定义)
union semun{
int val;
struct semid_ds *buf;
unsigned short *array;
}

4.程序源码

让两个进程分别向显示器(linux下一切皆文件,临界资源)打印AA和BB,当没有信号量进行保护时,会出现数据混乱,例如:“AABBABAAAB…”,为了解决这一问题,我们创建信号量进行保护。打印“AA”或“BB”

vi sem.c
#include<stdlib.h>
#include<stdio.h>
#include<unistd.h>
#include<sys/sem.h>

union semun{
	int val;
	struct semid_ds *buf;
	unsigned short *array;
	//struct seminfo *buff;	
};

static int set_semvalue(void);
static void del_semvalue(void);
static int semaphore_p(void);
static int semaphore_v(void);
static int sem_id;

int main(int argc, char *argv[])
{
	int i;
	int pause_time;
	char op_char = 'O';
	srand((unsigned int)getpid());

	sem_id =semget((key_t)1234, 1, 0666 | IPC_CREAT);

/* 如果程序第一个被调用,也就是调用时含有一个参数,使得argc>1,此时就调用set_semvalue初始化信号量,并将op_char设置为x*/
	if (argc > 1){
		if(!set_semvalue()){
			fprintf(stderr, "Failed to initialize semaphore\n");
			exit(EXIT_FAILURE);
		}
		op_char = 'X';
		sleep(2);
	}

/*进入和离开临界区10次,每次循环开始的时候首先调用semaphore_p函数,它在程序将进入临界区域时设置信号量以等待进入*/
	for(i=0; i<10; i++){
		if(!semaphore_p()) exit(EXIT_FAILURE);
		printf("%c", op_char);fflush(stdout);
		pause_time = rand() % 3;
		sleep(pause_time);
		printf("%c", op_char);fflush(stdout);

/*进入临界区域后,调用semaphore_v将信号量设置为可用,然后等待一段随机的时间,再进入下一次循环*/
		if(!semaphore_v()) exit(EXIT_FAILURE);
		pause_time = rand() % 2;
		sleep(pause_time);
	}
	printf("\n%d - finished\n", getpid());
	if (argc > 1)
	{
		sleep(10);
		del_semvalue();
	}
	exit(EXIT_SUCCESS);
}

/*该函数用来将semctl调用的command参数设置为SETVAL来初始化信号量*/
static int set_semvalue(void)
{
	union semun sem_union;
	
	sem_union.val = 1;
	if (semctl(sem_id, 0, SETVAL, sem_union)==-1) return 0;
	return (1);
}
/*通过调用semctl调用的command设置为IPC_RMID来删除信号量ID*/
static void del_semvalue(void)
{
	union semun sem_union;

	if (semctl(sem_id, 0, IPC_RMID, sem_union)==-1)
	fprintf(stderr, "Failed to delete semaphore");
}

/*对信号量执行减1操作*/
static int semaphore_p(void)
{
	struct sembuf sem_b;
	sem_b.sem_num = 0;
	sem_b.sem_op = -1;
	sem_b.sem_flg = SEM_UNDO;
	if (semop(sem_id, &sem_b, 1) == -1){
		fprintf(stderr,"semaphore_p failed\n");
		return (0);
	}
	return(1);
}

/*对信号量执行加1操作*/
static int semaphore_v(void)
{
	struct sembuf sem_b;
	sem_b.sem_num = 0;
	sem_b.sem_op = 1;
	sem_b.sem_flg = SEM_UNDO;
	if (semop(sem_id, &sem_b, 1) == -1){
		fprintf(stderr,"semaphore_v failed\n");
		return (0);
	}
	return(1);
}

编译运行
在这里插入图片描述

5.附代码2:父子进程间的信号量

与上述代码几乎相同,只不过变为了父子进程间的信号量机制。

vi sem2.c
#include<stdlib.h>
#include<stdio.h>
#include<unistd.h>
#include<sys/sem.h>
#include<sys/ipc.h>
#include<sys/types.h>

static int set_semvalue(void);
static void del_semvalue(void);
static int semaphore_p(void);
static int semaphore_v(void);
static int sem_id;

union semun{
    int val;
    struct semid_ds *buf;
    unsigned short *array;
    //struct seminfo *buff; 
};
int main(int argc, char *argv[])
{
    int i;
    int pause_time;
    char op_char = 'O';
    srand((unsigned int)getpid());

    sem_id =semget((key_t)1234, 1, 0666 | IPC_CREAT);
    int id = fork();
    if(id<0)
    {
        perror("fork failed\n");
        return -1;
    }

    else if (id>0){
        if(!set_semvalue()){
            fprintf(stderr, "Failed to initialize semaphore\n");
            exit(EXIT_FAILURE);
        }
        op_char = 'X';
        sleep(2);
    }
    for(i=0; i<10; i++){
        if(!semaphore_p()) exit(EXIT_FAILURE);
        printf("%c", op_char);fflush(stdout);
        pause_time = rand() % 3;
        sleep(pause_time);
        printf("%c", op_char);fflush(stdout);
        if(!semaphore_v()) exit(EXIT_FAILURE);
        pause_time = rand() % 2;
        sleep(pause_time);
    }
    printf("\n%d - finished\n", getpid());
    if (id> 0)
    {
        sleep(10);
        del_semvalue();
    }
    exit(EXIT_SUCCESS);
}

static int set_semvalue(void)
{
    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(void)
{
    union semun sem_union;

    if (semctl(sem_id, 0, IPC_RMID, sem_union)==-1)
    fprintf(stderr, "Failed to delete semaphore");
}
static int semaphore_p(void)
{
    struct sembuf sem_b;
    sem_b.sem_num = 0;
    sem_b.sem_op = -1;
    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(void)
{
    struct sembuf sem_b;
    sem_b.sem_num = 0;
    sem_b.sem_op = 1;
    sem_b.sem_flg = SEM_UNDO;
    if (semop(sem_id, &sem_b, 1) == -1){
        fprintf(stderr,"semaphore_v failed\n");
        return (0);
    }
    return(1);
}

编译运行结果:
在这里插入图片描述
注意:在这里我们可以看到子进程的pid为父进程的pid+1,注意的是fork创建子进程的返回值为0,和这里的pid是不同的。

参考:Linux程序设计第四版
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

爱学习的贝塔

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值