/**复习前面linux的命令知识:
touch test.c //创建一个文件
vi test.c //打开文件
gcc test.c //编译test.c程序
./a.out //运行程序
/
实验目的
1、熟悉PV操作的实现原理。
2、了解进程间通信机制。熟悉信号量机制。使用信号量机制模拟实现PV操作,从而控制多个进程对共享资源的使用。
实验内容
1、使用信号量机制来模拟实现PV操作。
//sem_p.h
#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/sem.h>
#include<stdlib.h>
#include<stdio.h>
typedef union semun{
int val;
struct semid_ds *buf;
unsigned short *array;
}semun;
//初始化信号量
int sem_init(int key)
{
int semid;
semun arg;
if((semid=semget(key,1,0660|IPC_CREAT|IPC_EXCL))<0) //创建包含 1 个信号量的信号量对象
{
perror("sem_init:semget error");
exit(1);
}
arg.val = 1;
if(semctl(semid,0,SETVAL,arg)<0)
{
perror("sem_init:semctl error");
exit(2);
};
return semid;
}
//信号量的 P 操作
int sem_p(int semid)
{
struct sembuf pbuf;
pbuf.sem_num=0;
pbuf.sem_op=-1;
pbuf.sem_flg=SEM_UNDO;
if(semop(semid,&pbuf,1)==-1)
{
perror("sem_p:semop error");
exit(3);
}
return 0;
}
//信号量的 V 操作
int sem_v(int semid)
{
struct sembuf vbuf;
vbuf.sem_num=0;
vbuf.sem_op=1;
vbuf.sem_flg=SEM_UNDO;
if(semop(semid,&vbuf,1)==-1)
{
perror("sem_v:semop error");
exit(3);
}
return 0;
}
//删除信号量
void sem_rmv(int semid)
{
if(semctl(semid,0,IPC_RMID)<-1)
{
perror("sem_rmv:semctl error");
exit(2);
}
}
//test.c
#include<unistd.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<sys/wait.h>
#include<string.h>
#include<stdio.h>
#include"sem_pv.h"
int main(void)
{
key_t key;
int pid,fd,semid,n;
char str[80];
key=ftok(".",5);
semid=sem_init(key);
fd=open("test.txt",O_RDWR|O_CREAT|O_TRUNC,0644);
while((pid=fork())==-1);
if(pid==0)
{
sleep(1);
sem_p(semid);
lseek(fd,SEEK_SET,0);
read(fd,str,sizeof(str));
sem_v(semid);
printf("child:read str from test file:%s\n",str);
exit(0);
}
else
{
sem_p(semid);
printf("parent:please enter a str for test file(strlen<80):\n");
gets(str);
n=strlen(str);
lseek(fd, SEEK_SET,0);
write(fd,str,n);
sem_v(semid);
wait(0);
close(fd);
sem_rmv(semid);
exit(0);
}
}
运行结果:
2、编写一个简单的程序,使用该PV模拟操作控制多进程对共享资源的使用。
//源代码:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#include<semaphore.h>
#define P sem_wait
#define V sem_post
#define full_apple &fullA
#define full_orange &fullB
#define empty &empty_aha
sem_t fullA;
sem_t fullB;
sem_t empty_aha;
int num=0;
void* Dad(void *p)
{
while(num<10)
{
P(empty);
num++;
printf("老爸放了个苹果%d\n",num);
V(full_apple);
}
}
void* Dangter(void *p)
{
while(num<10)
{
P(full_apple);
num++;
printf("女儿吃了个苹果%d\n",num);
V(empty);
}
}
void* Mum(void *p)
{
while(num<10)
{
P(empty);
num++;
printf("老妈放了个橘子%d\n",num);
V(full_orange);
}
}
void* Son(void *p)
{
while(num<10)
{
P(full_orange);
num++;
printf("儿子吃了个橘子%d\n",num);
V(empty);
}
}
int main()
{
sem_init(full_apple, 0, 0);
sem_init(full_orange, 0, 0);
sem_init(empty, 0, 1);
pthread_t tid0;
pthread_t tid1;
pthread_t tid2;
pthread_t tid3;
pthread_create(&tid0, NULL, Dad, NULL);
pthread_create(&tid1, NULL, Mum, NULL);
pthread_create(&tid2, NULL, Son, NULL);
pthread_create(&tid3, NULL, Dangter, NULL);
getchar();
pthread_exit(0);
return 0;
}
运行结果:
思考题
1、参考程序中的sem_p (),sem_v()是原子操作吗?这种模拟能达到预期效果吗?它们与实验三中的lock()与unlock()有何异同?
答:是,可以达到预期的效果。Sem_p(),sem_v()程序不能中断,多个进程不能同时执行,一个信号量只能置一次初值,以后只能对其进行p操作或者操作,而这都可以达到进程互斥的目的。
2、请思考程序代码中sleep(1)的作用。
答:父进程先于子进程执行完毕,父进程执行完了之后子进程就会成为一个孤儿进程,孤儿进程由系统进行回收,所以它是不占用资源的。而如果让子进程先于父进程执行完成,那么子进程就会成为一个僵死进程,僵死进程会一直等待父进程的回收,但是如果父进程是一个循环,不会结束;那么子进程就会一直保持僵死状态,而僵死是占用系统列表中的资源的,假如系统中有大量的僵死进程存在,系统就会因缺乏资源而导致崩溃。Sleep(1)在这个地方的作用也是为了避免和防范僵死进程的产生。