作业:使用信号灯循环输出ABC
sem.c
#include <head.h>
union semun {
int val; /* Value for SETVAL */
struct semid_ds *buf; /* Buffer for IPC_STAT, IPC_SET */
unsigned short *array; /* Array for GETALL, SETALL */
struct seminfo *__buf; /* Buffer for IPC_INFO
(Linux-specific) */
};
//初始化函数
int init_sem(int semid, int semno)
{
int val = -1;
printf("请输入编号为%d的灯的初始值:", semno);
scanf("%d", &val);
getchar(); //吸收scanf留下的回车
//对当前灯进行赋初始值
//定义一个共用体变量
union semun us;
us.val = val; //要赋的值
if(semctl(semid, semno, SETVAL, us) == -1)
{
perror("semctl error\n");
return -1;
}
//成功返回0
return 0;
}
//信号灯集的申请,初始化信号灯,并返回信号灯集的id
int create_sem(int semcount)
{
//1.创建key值
key_t key = ftok("/", 'y');
if(key == -1)
{
perror("ftok error\n");
return -1;
}
//2.通过key值创建一个信号灯集
int semid = semget(key, semcount, IPC_CREAT|IPC_EXCL|0664);
if(semid == -1)
{
if(errno == EEXIST)
{
//信号灯集已经存在,无需创建直接打开即可
semid = semget(key, semcount, IPC_CREAT);
return semid; //之后的进程打开信号灯集只需返回id即可,无需进行初始化操作
}
perror("semget error\n");
return -1;
}
//3.给信号灯集中的信号灯进行初始化操作
for(int i=0;i<semcount;i++)
{
init_sem(semid, i); //初始化编号为i的灯value值
}
//4.返回信号灯集的id
return semid;
}
//申请信号灯资源操作P操作
int P(int semid, int semno)
{
//定一个操作的结构体变量
struct sembuf buf;
buf.sem_num = semno; //要操作的灯的编号
buf.sem_op = -1; //表示申请资源操作
buf.sem_flg = 0; //如果没有资源,则阻塞等待
//调用semop函数完成P操作
if(semop(semid, &buf, 1) == -1)
{
perror("semop error\n");
return -1;
}
return 0; //成功返回0
}
//释放信号灯资源操作 v操作
int V(int semid, int semno)
{
//定一个操作的结构体变量
struct sembuf buf;
buf.sem_num = semno; //要操作的灯的编号
buf.sem_op = 1; //表示释放资源操作
buf.sem_flg = 0; //如果没有资源,则阻塞等待
//调用semop函数完成V操作
if(semop(semid, &buf, 1) == -1)
{
perror("semop error\n");
return -1;
}
return 0; //成功返回0
}
//信号灯集的删除
int del_sem(int semid)
{
if(semctl(semid, 0, IPC_RMID, 0) == -1)
{
perror("del error\n");
return -1;
}
return 0;
}
主函数
#include <head.h>
#include "sem.h"
#define PAGE_SIZE 4096
int main(int argc, const char *argv[])
{
//创建一个信号灯集
int semid = create_sem(3); //创建一个信号灯集包含两个灯,并初始化
if(semid == -1)
{
perror("create_sem error\n");
return -1;
}
//创建的key值
key_t key = ftok("/", 'y');
if(key == -1)
{
perror("ftok error\n");
return -1;
}
printf("key = %#x\n", key);
//通过key值创建共享内存段
int shmid = shmget(key, PAGE_SIZE, IPC_CREAT|0664);
if(shmid == -1)
{
perror("shmget error\n");
return -1;
}
printf("shmid = %d\n", shmid);
//将共享内存段映射到用户空间
char *addr = (char *)shmat(shmid, NULL, 0);
if(addr == (void *)-1)
{
perror("shmat error\n");
return -1;
}
printf("addr = %p\n", addr); //输出映射的虚拟地址
pid_t pid = -1;
pid = fork();
if(pid > 0)
{
pid_t pid2 = fork();
if(pid2 > 0)
{
//父进程
int count =0;
while(1)
{
//V操作,释放1灯的资源
P(semid,0);
printf("A");
count++;
fflush(stdout); //每个进程的用户空间相互独立,缓冲区不同
V(semid,1);
if(count == 5)
{
break;
}
}
wait(NULL);
wait(NULL);
wait(NULL);
}
else if(pid2 == 0)
{
//子进程2
int count=0;
while(1)
{
//P操作,等待0灯的资源
P(semid, 2);
printf("C\n");
count++;
V(semid, 0);
if(count == 5)
{
break;
}
}
exit(EXIT_SUCCESS);
}
else
{
perror("fork2 error\n");
return -1;
}
}
else if(pid == 0)
{
//子进程1
int count=0;
while(1)
{
//P操作,等待1号灯的资源
P(semid, 1);
printf("B");
count++;
fflush(stdout);
//V操作,释放0号灯的资源
V(semid,2);
if(count == 5)
{
break;
}
}
exit(EXIT_SUCCESS);
}
else
{
perror("fork1 error\n");
return -1;
}
if(shmdt(addr) == -1)
{
perror("shmdt error\n");
return -1;
}
del_sem(semid);
return 0;
}
现象