SystemV 信号量(二)—— 使用SystemV信号量来控制父子进程间通信

如果说 SystemV 共享内存提供了进程间通信的渠道,SystemV 信号量则是提供了进程间通信的控制方法。通过申请信号量,来获取临界资源的使用权;通过释放信号量来归还临界资源的使用权。

下面将使用两个信号量来控制进程间通信,一个是写信号量,代表可写位置的个数;一个是读信号量,代表可读内容的个数。

  • 写进程:
    • 申请一个位置来写 —— P操作(申请写信号量)
    • 执行写任务
    • 写完以后就多出一份可读内容
    • 释放一个可读内容 —— V操作(释放读信号量)
  • 读进程:
    • 申请一份内容来读 —— P操作(申请读信号量)
    • 执行读操作
    • 读完以后就多出一个可写的位置
    • 释放一个可写位置 —— V操作(释放写信号量)

         目录

1、创建并挂接共享内存

2、创建并初始化信号量集合

3、创建父子进程

(1) PV操作

(2) 父子进程通过共享内存实现进程间通信

4、完整代码


1、创建并挂接共享内存

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

// 新建共享内存
int shmid = shmget(key, 4*1024, IPC_CREAT | IPC_EXCL | 0666);
if (shmid < 0)
{
    perror("shmget");
    return 1;
}
// 把当前进场挂接到共享内存上
char* shmaddr = (char*)shmat(shmid, NULL, 0);
if(shmaddr == (void*)-1)
{
	perror("shmat");
	return 1;
}

2、创建并初始化信号量集合

// 全局变量
enum SEM
{
	SEM_WR,
	SEM_RD
};


// 创建信号量集合
int semid = semget(key, 2, IPC_CREAT | 0666);
if (semid < 0) 
{
	perror("semget");
	return 1;
}

// 初始化信号量集合
union semun mysem;
mysem.val = 1;
semctl(semid, SEM_WR, SETVAL, mysem);	
mysem.val = 0;
semctl(semid, SEM_RD, SETVAL, mysem);

3、创建父子进程

父进程负责写,子进程负责读,在此之前,我们可以先把PV操作封装成两个函数,方便后续调用。

(1) PV操作

// P操作
void Poperation(int semid, int semnum)
{
	struct sembuf buf;
	buf.sem_num = semnum;
	buf.sem_op = -1;
	buf.sem_flg = 0;

	semop(semid, &buf, 1);
}

// V操作
void Voperation(int semid, int semnum)
{
	struct sembuf buf;
	buf.sem_num = semnum;
	buf.sem_op = 1;
	buf.sem_flg = 0;
	
	semop(semid, &buf, 1);
}

(2) 父子进程通过共享内存实现进程间通信

// 创建子进程,让父子进程通过共享内存通信,通过信号量控制互斥
int pid = fork();
if(pid < 0)
{
	perror("fork");
	shmctl(shmid, IPC_RMID, NULL);
	semctl(semid, 0, IPC_RMID);	
	return 1;
}
else if(pid > 0)
{
	// 父进程
	int i = 0;
	while(1)
	{
		Poperation(semid, SEM_WR);  // 申请写信号量,可写的位置--
		strcpy(shmaddr+i, "a");
		i++;
		Voperation(semid, SEM_RD); // 释放读信号量,可读的内容++
		sleep(1);
    }
}
else
{
	// 子进程
	while(1)
	{
	    Poperation(semid, SEM_RD);  // 申请读信号量,可读的内容--
		printf("%s\n",shmaddr);
		Voperation(semid, SEM_WR); // 释放写信号量,可写的位置++
		sleep(1);
	}
}

4、完整代码

#include 
#include 
#include 
#include 
#include 
#include 
#include 

enum SEM
{
	SEM_WR,
	SEM_RD
};

union semun{
	int val;
};

void Poperation(int semid, int semnum)
{
	struct sembuf buf;
	buf.sem_num = semnum;
	buf.sem_op = -1;
	buf.sem_flg = 0;

	semop(semid, &buf, 1);
}

void Voperation(int semid, int semnum)
{
	struct sembuf buf;
	buf.sem_num = semnum;
	buf.sem_op = 1;
	buf.sem_flg = 0;
	
	semop(semid, &buf, 1);
}

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

	// 创建信号量集合
	int semid = semget(key, 2, IPC_CREAT | 0666);
	if (semid < 0) 
	{
		perror("semget");
		return 1;
	}

	// 初始化信号量集合
	union semun mysem;
	mysem.val = 1;
	semctl(semid, SEM_WR, SETVAL, mysem);	
	mysem.val = 0;
	semctl(semid, SEM_RD, SETVAL, mysem);

	// 新建共享内存
	int shmid = shmget(key, 4*1024, IPC_CREAT | IPC_EXCL | 0666);
	if (shmid < 0)
	{
		perror("shmget");
		return 1;
	}
	// 把当前进场挂载到共享内存上
	char* shmaddr = (char*)shmat(shmid, NULL, 0);
	if(shmaddr == (void*)-1)
	{
		perror("shmat");
		return 1;
	}

	// 创建子进程,让父子进程通过共享内存通信,通过信号量控制互斥
	int pid = fork();
	if(pid < 0)
	{
		perror("fork");
		shmctl(shmid, IPC_RMID, NULL);
		semctl(semid, 0, IPC_RMID);	
		return 1;
	}
	else if(pid > 0)
	{
		// 父进程
		int i = 0;
		while(1)
		{
			Poperation(semid, SEM_WR);  // 申请写信号量,可写的位置--
			strcpy(shmaddr+i, "a");
			i++;
			Voperation(semid, SEM_RD); // 释放读信号量,可读的内容++
			sleep(1);
		}
	}
	else
	{
		// 子进程
		while(1)
		{
			Poperation(semid, SEM_RD);  // 申请读信号量,可读的内容--
			printf("%s\n",shmaddr);
			Voperation(semid, SEM_WR); // 释放写信号量,可写的位置++
			sleep(1);
		}
	}

	// 释放共享内存
	shmctl(shmid, IPC_RMID, NULL);

	// 删除信号量集合
	semctl(semid, 0, IPC_RMID);	
	return 0;
}

你可能感兴趣的:(Linux,进程与线程,linux)