day16 信号灯

本文介绍了信号灯(信号量)作为进程间同步的机制,详细讲解了P操作(申请资源)和V操作(释放资源),以及Posix有名信号灯和无名信号灯的使用方法,包括如何初始化、打开、关闭和删除。此外,还提到了SystemV信号灯的创建和操作,展示了相关的C语言代码示例,用于演示如何在多线程或进程中实现资源的协调访问。
摘要由CSDN通过智能技术生成

信号灯概念和有名信号灯

目录

信号灯概念和有名信号灯

有名信号灯

无名信号灯

信号灯P操作

信号灯V操作

system V信号灯的


 

信号灯/信号量(semaphore)

信号量代表某一类资源,其值表示系统中该资源的数量;

信号量是一个受保护的变量,只能通过三种操作来访问

初始化、P操作(申请资源)、V操作(释放资源)(P消费者、V生产者)

概念:

是不同进程间或一个给定进程内部不同线程间同步的机制。类似我们的PV操作概念:

生产者和消费者场景
P(S) 含义如下:
     if  (信号量的值大于0) {  
 申请资源的任务继续运行;
           信号量的值减一;
} else {   
申请资源的任务阻塞;
} 
V(S) 含义如下:
     信号量的值加一;
     if (有任务在等待资源) {   
唤醒等待的任务,让其继续运行 
}

三种信号灯:

Posix  有名信号灯;

Posix  无名信号灯;

System V 信号灯;

Posix 有名信号灯和无名信号灯使用:

有名信号灯

有名信号灯打开:

sem_t *sem_open(const char *name, int oflag);
sem_t*sem_open(const char *name, int oflag, mode_t mode, unsigned int value);

参数:

name:name是给信号灯起的名字;

oflag:打开方式,常用O_CREAT

mode :文件权限,常用06666

value:信号量值。二元信号量值为1,普通表示资源数目;

信号灯文件为止:/dev/shm/

有名信号灯关闭:

int sem_close(sem_t *sem);

有名信号灯的删除:

int sem_unlink(const char* name);

代码实现:

mysem_w:

#include <stdio.h>

#include <fcntl.h>
#include <sys/stat.h>
#include <semaphore.h>

#include <stdlib.h>
#include <unistd.h>
#include <sys/ipc.h>
#include <sys/shm.h>

#include <signal.h>

void delsemfile(int sig) {
	sem_unlink("mysem_w");
	exit(0);
}

int main() {

//捕捉信号,按下Ctrl+c时删除myser_w
	struct sigaction act;
	act.sa_handler = delsemfile;
	act.sa_flags = 0;
	sigemptyset(&act.sa_mask);

	sigaction(SIGINT, &act, NULL);

//创建共享内存

	//1 申请共享内存key值;
    key_t key;
	key = ftok(".", 100);
	if (key < 0) {
		perror("ftok");
		return 0;
	}

	//2 根据key值创建共享内存
    int shmid;
	shmid = shmget(key, 500, 0666|IPC_CREAT);
	if (shmid < 0) {
		perror("shmget");
		return 0;
	}

	//3 获取共享内存地址
    char *shmaddr;
	shmaddr = shmat(shmid, NULL, 0);

	//定义读写信号量
    sem_t *sem_r, *sem_w;

	//打开信号量;(初始化 读信号量为0,写信号量为1)
	sem_r = sem_open("mysem_r", O_CREAT|O_RDWR, 0666, 0);
	sem_w = sem_open("mysem_w", O_CREAT|O_RDWR, 0666, 1);

//向共享内存中写数据

	//1 判断是否能写
	while(1) {
		sem_wait(sem_w);    //sem_w--
		printf(">");
		fgets(shmaddr, 500, stdin);
		sem_post(sem_r);    //sem_r++
	}



}

 mysem_r:

#include <stdio.h>

#include <fcntl.h>
#include <sys/stat.h>
#include <semaphore.h>

#include <stdlib.h>
#include <unistd.h>
#include <sys/ipc.h>
#include <sys/shm.h>

#include <signal.h>

void delsemfile(int sig) {
	sem_unlink("mysem_r");
	exit(0);
}


int main() {

//捕捉信号,按下Ctrl+c时删除myser_r
	struct sigaction act;
	act.sa_handler = delsemfile;
	act.sa_flags = 0;
	sigemptyset(&act.sa_mask);

	sigaction(SIGINT, &act, NULL);

//创建共享内存

	//1 申请共享内存key值;
    key_t key;
	key = ftok(".", 100);
	if (key < 0) {
		perror("ftok");
		return 0;
	}

	//2 根据key值创建共享内存
    int shmid;
	shmid = shmget(key, 500, 0666|IPC_CREAT);
	if (shmid < 0) {
		perror("shmget");
		return 0;
	}

	//3 获取共享内存地址
    char *shmaddr;
	shmaddr = shmat(shmid, NULL, 0);

	//定义读写信号量
    sem_t *sem_r, *sem_w;

	//打开信号量;(初始化 读信号量为0,写信号量为1)
	sem_r = sem_open("mysem_r", O_CREAT|O_RDWR, 0666, 0);
	sem_w = sem_open("mysem_w", O_CREAT|O_RDWR, 0666, 1);

//向共享内存中写数据

	//1 判断是否能写
	while(1) {
		sem_wait(sem_r);    //sem_r--

		printf("%s\n", shmaddr);

		sem_post(sem_w);    //sem_w++
	}



}

无名信号灯

无名信号灯在Linux下只支持线程间同步不支持进程间同步

无名信号灯的初始化

int sem_close(sem_t *sems, int shared, unsigned int value);

参数:

sem:需要初始化的信号灯变量;

shared:shared指定为0,表示信号量只能由这个初始化这个信号量的进程使用,不能在进程间使用,Linux不支持进程间同步;

value:信号量的值

无名信号灯的销毁:

int sem_destroy(sem_t *sem);

信号灯P操作

int sem_wait(sem_t *sem);

获取资源,如果信号量为0,表示这时没有相应资源空闲,那么调用线程就将挂起,直到有空闲资源可以获取

信号灯V操作

int sem_post(sem_t *sem);

释放资源,如果没有线程阻塞在该sem上,表示没有线程等待该资源,这时该函数就对信号量的值进行增1操作,表示同类资源多增加了一个。如果至少有一个线程阻塞在该sem上,表示有线程等待资源,信号量为0,这时该函数保持信号量为0不变,并使某个阻塞在该sem上的线程从sem_wait函数中返回

注意:编译posix信号灯需要加pthread动态库。

代码实现

#include <stdio.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <semaphore.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <signal.h>
#include <pthread.h>

sem_t sem_r, sem_w;
char *shmaddr;

void deseroysem(int sig) {
	sem_destroy(&sem_r);
	sem_destroy(&sem_w);
	exit(0);
}

void *readmem(void *arg) {
	while(1) {
		sem_wait(&sem_r);
		printf("%s\n", shmaddr);
		sem_post(&sem_w);
	}
}

int main() {

//捕捉信号,按下Ctrl+c时删除myser_w
	struct sigaction act;
	act.sa_handler = deseroysem;
	act.sa_flags = 0;
	sigemptyset(&act.sa_mask);

	sigaction(SIGINT, &act, NULL);

//创建共享内存

	//1 申请共享内存key值;
	key_t key;
	key = ftok(".", 100);
	if (key < 0) {
		perror("ftok");
		return 0;
	}

	//2 根据key值创建共享内存
	int shmid;
	shmid = shmget(key, 500, 0666|IPC_CREAT);
	if (shmid < 0) {
		perror("shmget");
		return 0;
	}

	//3 获取共享内存地址
	shmaddr = shmat(shmid, NULL, 0);
	
	//无名信号灯初始化;
	sem_init(&sem_r, 0, 0);
	sem_init(&sem_w, 0, 1);

	//创建线程
	pthread_t tid;
	pthread_create(&tid, NULL, readmem, NULL);

	//向共享内存中写数据
	while(1) {
		sem_wait(&sem_w);    //sem_w--
		printf(">");
		fgets(shmaddr, 500, stdin);
		sem_post(&sem_r);    //sem_r++
	}



}

system V信号灯的

int semget*key_t key, int nsems, int semflg);

功能:创建 / 打开信号灯;

参数:

key:ftok 产生的key值;(和信号灯关联的key值)

nsems :信号灯集 中包含的信号灯数目;

semflg :信号灯集 的访问权限,通常是IPC_CREAT | 0666

返回值:成功返回信号灯集id, 失败返回-1;

int semop(int semid, struct sembuf *opsptr, size_t nops);

功能:对信号灯集合中的信号量进行p  -  v操作

参数:

semid:信号灯集合 的id;

struct sembuf {

                short sem_num;         //要操作的信号灯的编号;

                short sem_op;            //1:释放资源,V操作; -1:分配资源, P操作;

                short sem_flg;            // 0(阻塞),IPC_NOWAT,SEM_UNDO

};                               //对某一个信号灯的操作,如果同时对多个操作,则需要定义这种结构以数组

nops:要操作的信号灯的个数, 1个;

返回值:成功:0,; 失败:-1.

int semctl(int semid, int semnum, int cmd.../*union semun arg*/);

功能:信号灯集合的控制(初始化 / 删除)

参数:

semid:信号灯集id

semnum: 要操作的集合中的信号灯编号

   cmd:

   GETVAL:获取信号灯的值,返回值是获得值

   SETVAL:设置信号灯的值,需要用到第四个参数:共用体

   IPC_RMID:从系统中删除信号灯集合

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

代码实现:

#include <stdio.h>
#include <semaphore.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>

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

#define SEM_READ 0
#define SEM_WRITE 1

union semun{
	int val;
};

//p操作
void Poperation(int semid, int semindex) {
	struct sembuf sbuf;
	sbuf.sem_num = semindex;
	sbuf.sem_op = -1;
	sbuf.sem_flg = 0;

	semop(semid, &sbuf, 1);


}
//v操作
void Voperation(int semid, int semindex ) {

	struct sembuf sbuf;
	sbuf.sem_num = semindex;
	sbuf.sem_op = 1;
	sbuf.sem_flg = 0;

	semop(semid, &sbuf, 1);

}



int main() {
	key_t key;
	int semid;
	int shmid;
	char *shmaddr;

	key = ftok(".", 100);
	if (key < 0) {
		perror("ftok");
		return 0;
	}

	semid = semget(key, 2, IPC_CREAT|0666);
	if (semid < 0) {
		perror("semget");
		return 0;
	}

	shmid = shmget(key, 500, IPC_CREAT|0666);
	shmaddr = shmat(shmid, NULL, 0);

	//初始化
	union semun mysem;
	mysem.val = 0;
	semctl(semid, SEM_READ, SETVAL, mysem);
	mysem.val = 1;
	semctl(semid, SEM_WRITE, SETVAL, mysem);


	pid_t pid;
	pid = fork();
	if (pid < 0) {
		perror("fork");
		shmctl(shmid, IPC_RMID, NULL);
		semctl(semid, 0, IPC_RMID);
		exit(-1);
	}else if(pid == 0) {
		while(1){
			Poperation(semid, SEM_READ);

			printf("%s\n", shmaddr);

			Voperation(semid, SEM_WRITE);
		}

	}else{
		while(1){
			Poperation(semid, SEM_WRITE);

			printf(">");
			fgets(shmaddr, 32, stdin);

			Voperation(semid, SEM_READ);
		} 
	}


}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值