进程通信4--信号量(Semaphore)

信号量是什么?

(1)信号量本质上是一个具有原子性的计数器,用来描述临界资源的,不能用全局变量count加加减减替换(因为他没有原子性)。
(2)信号量以保护临界资源为目的,但他本身也是个临界资源;他控制多个进程对共享资源的访问,通常描述临界资源当中,临界资源的数量,常常被当做锁来使用,防止一个进程访问另外一个进程正在使用的资源
(3)其中最简单的信号量=1,也叫做二元信号量(互斥锁),可控制单个资源,只能取0和1;信号量>=2为多元信号量

只要理理解上面三句话,应该结合代码理解就不难了。

代码实现相关函数:

#include<sys/types.h>
#include<sys/ipc.h>
key_t ftok(const char* pathname,int id)//pathname:路径名  id:项目id,非0整数(只有低8位有效)
//2.semget函数
int semget(key_t key,int nsems,int semflg);
//参数:key-->信号集的名字
//nsems:信号集中信号量的个数
//semflg:由九个权限构成,用法和创建文件时使用的mode模式一样
//返回值:成功返回一个非负整数,即该信号集的标识码;失败返回-1

创建成功后,Linux下通过ipcs -s可以查看已经创建的信号量情况,删除使用删除:ipcrm -s semid

//semctl函数
int semctl(int semid,int semnum,int cmd,...);
//参数:
//semid:mesget返回的信号集标识码
//semnum:信号集中信号量的序号
//cmd:将要采取的动作(有三个取值,一般用于销毁)
//最后一个参数根据命令不同而不同
//返回值:成功返回0;失败返回-1

代码实测;
我们用两个进程分别对一个文件进行读写:
1.sem.h

#pragma once
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/sem.h>
#include <unistd.h>
#include <fcntl.h>
#include <unistd.h>
#include<cstdlib>
union semun
{
    
    
	int val;
};
void sem_init();//初始化
void sem_p();//P操作 +1
void sem_v();//V操作 -1
void sem_destroy();//销毁

2.sem.cpp

#include "sem.h"
static int semid = 0;

void sem_init()
{
    
    
	semid = semget((key_t)1234, 1, IPC_CREAT | IPC_EXCL | 0600);//不存在则创建,已经存在返回-1
	if (semid == -1)
	{
    
    
		semid = semget((key_t)1234, 1, IPC_CREAT | 0600);//返回-1之后获取他的semid
		if (semid == -1)
		{
    
    
			printf("semget error");
		}
	}
	else
	{
    
    
		 semun a;//a传值
		a.val = 1;
		if (semctl(semid, 0, SETVAL, a) == -1)//0代表信号量下标,设置这个下标的值
		{
    
    
			perror("semctl init error");
		}
	}
}

void sem_p()
{
    
    
	struct sembuf buf;
	buf.sem_num = 0;对下标为0的进行操作,刚刚设置了
	buf.sem_op = -1;//p操作,若val<0进行V操作信号量值加val,即a.val=a.val-1
	buf.sem_flg = SEM_UNDO;//设置在进程出现错误时信号量值自动恢复,防止一个进程占着信号量
	if (semop(semid, &buf, 1) == -1)
	{
    
    
		perror("p error");
	}
}

void sem_v()
{
    
    
	struct sembuf buf;
	buf.sem_num = 0;
	buf.sem_op = 1; //若val > 0进行V操作信号量值加val,即a.val = a.val + 1
	buf.sem_flg = SEM_UNDO;
	if (semop(semid, &buf, 1) == -1)
	{
    
    
		perror("v error");
	}
}

void sem_destroy()
{
    
    
	if (semctl(semid, 0, IPC_RMID) == -1)
	{
    
    
		perror("semctl destroy error");
	}
	printf("sem destory success!\n");
}



3.proA.cpp

#include"sem.h"
#include<iostream>
using namespace std;
void file_write(int x);
int main()
{
    
    
	 int a = 0;
		sem_init();
		for (int i = 0; i < 20; i++)
		{
    
    
			sem_p();
			a++;
			file_write(a);
			sem_v();
			sleep(1);
		}
		sleep(2);
		destory_sem();//由进程A来销毁这个信号量jin
	return 0;
}
void file_write(int x)
{
    
    
	const char*fpath = "/home/ubuntu/test.txt";
	FILE* file = fopen(fpath, "a+");//打开可读可写文件,不存在则创建
	char buf[11] = "A:write:";//方便区分
	buf[8] = x + '0';
	buf[9] = '\n';
	buf[10] = '\0';
	fwrite(buf, 1, strlen(buf), file);
	fclose(file);

}

4.proB.cpp

#include"sem.h"
#include<iostream>
using namespace std;
void file_write(int x);
int main()
{
    
    
	 int a = 0;
		sem_init();
		for (int i = 0; i < 20; i++)
		{
    
    
			sem_p();
			a++;
			file_write(a);
			sem_v();
			sleep(1);
		}
	return 0;
}
void file_write(int x)
{
    
    
	const char*fpath = "/home/ubuntu/test.txt";
	FILE* file = fopen(fpath, "a+");//打开可读可写文件,不存在则创建
	char buf[11] = "B:write:";//方便区分
	buf[8] = x + '0';
	buf[9] = '\n';
	buf[10] = '\0';
	fwrite(buf, 1, strlen(buf), file);
	fclose(file);

}

测试结果:
在这里插入图片描述

Guess you like

Origin blog.csdn.net/qq_40861091/article/details/102375358