关于信号量的基础理论知识请看:https://blog.csdn.net/qq_42418668/article/details/95166917
使用信号量和管道
实现A进程写内容到管道中,B进程不立刻的读取而是等A进程写完并且退出后B进程才读取。这里的A进程和B进程使用信号量来实现这个效果。
我们对semget semop semctl进行封装如下
sem.h
#ifndef __SEM_H
#define __SEM_H
typedef union semun
{
int val;
}Semun;
int sem_init(int key,int nsems,int semval[]);//return semid
void sem_p(int semid,int sems[],int size);
void sem_v(int semid,int sems[],int size);
void sem_del(int semid);
#endif
sem.c:
#include "sem.h"
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/sem.h>
int sem_init(int key,int nsems,int semval[])//return semid
{
int semid = semget((key_t)key,0,0664);
if(semid != -1)
{
return semid;
}
semid = semget((key_t)key,nsems,IPC_CREAT|0664);
assert(semid!=-1);
int i=0;
for(;i<nsems;++i)
{
Semun sem;
sem.val = semval[i];
semctl(semid,i,SETVAL,sem);
}
return semid;
}
void sem_p(int semid,int sems[],int size)
{
struct sembuf buff[size];
int i = 0;
for(;i<size;++i)
{
buff[i].sem_num = sems[i];
buff[i].sem_op = -1;
buff[i].sem_flg = SEM_UNDO;
}
semop(semid,buff,size);
}
void sem_v(int semid,int sems[],int size)
{
struct sembuf buff[size];
int i = 0;
for(;i<size;++i)
{
buff[i].sem_num = sems[i];
buff[i].sem_op = 1;
buff[i].sem_flg = SEM_UNDO;
}
semop(semid,buff,size);
}
void sem_del(int semid)
{
if(-1 == semctl(semid,0,IPC_RMID))
{
printf("delete error\n");
exit(0);
}
}
进程A的相关代码:
/*================================================================
* Copyright (C) 2019 . All rights reserved.
*
* 文件名称:main.c
* 创 建 者:gaojie
* 创建日期:2019年07月08日
* 描 述:
*
================================================================*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <assert.h>
#include "sem.h"
#include <fcntl.h>
int main()
{
int val=0;
int semid = sem_init(1234,1,&val);
int fd = open("FIFO",O_WRONLY);
assert(fd != -1);
while(1)
{
printf("please input:\n");
char buff[128]={0};
fgets(buff,128,stdin);
if(strncmp(buff,"end",3)==0)
{
break;
}
write(fd,buff,strlen(buff)-1);
}
int sem = 0;
sem_v(semid,&sem,1);
close(fd);
}
进程B的相关代码
/*================================================================
* Copyright (C) 2019 . All rights reserved.
*
* 文件名称:mainb.c
* 创 建 者:gaojie
* 创建日期:2019年07月08日
* 描 述:
*
================================================================*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <assert.h>
#include <fcntl.h>
#include "sem.h"
int main()
{
int val = 0;
int semid = sem_init(1234,1,&val);
int fd = open("FIFO",O_RDONLY);
printf("open FIFO success\n");
assert(-1!=fd);
int sem = 0;
sem_p(semid,&sem,1);
char buff[1024]={0};
int n =read(fd,buff,1023);
if(n<=0)
{
printf("error\n");
exit(0);
}
printf("data length = %d \n",strlen(buff));
printf("%s\n",buff);
close(fd);
}
如果我们注释掉进程A和进程B的关于信号量的相关操作
可以看到我们的进程A输入完一次之后,进程B接受到并且退出,由于管道必须有一段读,一段写才可以正常工作,所以B退出后A也随着B的退出而退出。
但是我们加上信号量"阻塞"进程B的接受数据,此时进程B必须要等待进程A释放临界区资源才可以进入临界区代码。所以最后的结果是进程B一直等待进程A将临界区资源释放,而A释放临界区资源的终止条件就是往管道中写入数据完毕,当A完成向管道中的写入数据操作后,进程A进行了v操作,将由于等待临界区资源而被挂起的进程B重新唤醒,然后B读到管道中的数据然后输出。整个过程完毕。