进程间通信
一. 实验目的
1.了解Linux操作系统用户进程的编程。
2.了解Linux操作系统中进程间通信系统调用的使用方法。
二. 实验内容
编写一个生产者-消费者程序组,支持多个生产者和消费者,生产者进程数为2,
消费者进程数为3。
生产者与消费者之间使用共享内存进行数据传递,并使用信号量对数据的内容进行保护。
具体要求如下:
-
共享内存大小为3字节,用于存储生产者产生的数据(字符)。如果其中某个单元存储的字符为’0’,表示空闲,可以写入。
-
2个生产者:
随机等待一段时间(1-5秒),往缓冲区添加一个数据,数据为‘1’-‘9’之间的随机字符。如果缓冲区已满,需要等待消费者取走数据后再添加。
完成写入6个数据后退出。 -
3个消费者
随机等待一段时间,从缓冲区读取数据,读完之后,把字符改为‘0’。
若缓冲区为空,等待生产者添加数据后再读取
完成读取4个数据后退出。 -
每次往共享内存缓冲区读写数据时,需要在终端输出相应的提示信息,包括:进程id,读(写)时间(时分秒),读(写)前缓冲区字符情况,生产消费数据情况,如:
生产者***(pid)14:10:01 809 生产数据1
消费者*** 14:10:05 819 消费数据8
2.S1-可读(0) S2-可写(3),S3-可访问共享内存(1)
对于生产者:
P(S2)
P(S3)
写数据
V(S3)
V(S1)
对于消费者:
P(S1)
P(S3)
读数据
V(S3)
V(S2)
忽略英文注释。。懒得删。。
消费者
consumer.c
#include <time.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <sys/shm.h>
#include "semun.h"
struct share_memory{
char a;
char b;
char c;
};
int semaphore_p(int sem_id);
int semaphore_v(int sem_id);
int set_semvalue(int sem_id,int v);
void del_semvalue(int sem_id);
int main(int argc, char *argv[])
{
int tpid=fork();
if(tpid==0) fork();
int i;
int pause_time;
int sem_id,sem_idR,sem_idW;
void * shared_memory =(void *)0;
struct share_memory * shared_stuff;
sem_idR = semget((key_t)12340, 1, 0666 | IPC_CREAT);
sem_idW = semget((key_t)12350, 1, 0666 | IPC_CREAT);
sem_id = semget((key_t)12360, 1, 0666 | IPC_CREAT);
if(!set_semvalue(sem_idR,0)||!set_semvalue(sem_idW,3)||!set_semvalue(sem_id,1)){
fprintf(stderr,"Failed to initialize semaphore\n");
exit(EXIT_FAILURE);
}
int shmid;
shmid=shmget((key_t)12370,sizeof(struct share_memory),0666|IPC_CREAT);
if(shmid == -1){
fprintf(stderr,"shmget failed\n");
exit(EXIT_FAILURE);
}
shared_memory = shmat(shmid, (void *)0, 0);
if (shared_memory == (void *)-1) {
fprintf(stderr, "shmat failed\n");
exit(EXIT_FAILURE);
}
shared_stuff = (struct share_memory *)shared_memory;
//因为先运行消费者,所以初始化等工作理应放在消费者这里
shared_stuff->a='0';
shared_stuff->b='0';
shared_stuff->c='0';
int cnt=0;
while(1){
srand((unsigned int)time(NULL));
if(cnt>=4){
printf("\n%d - finished\n", getpid());
break;
}
if (!semaphore_p(sem_idR)) exit(EXIT_FAILURE);
/*if(shared_stuff->a=='0'&&shared_stuff->b=='0'&&shared_stuff->c=='0'){
pause_time = rand() % 5+1;
sleep(pause_time);
}*/
if (!semaphore_p(sem_id)) exit(EXIT_FAILURE);
cnt++;
char newConsum='0';
if(shared_stuff->a!='0'){
newConsum=shared_stuff->a;
shared_stuff->a='0';
}else if(shared_stuff->b!='0'){
newConsum=shared_stuff->b;
shared_stuff->b='0';
}else if(shared_stuff->c!='0'){
newConsum=shared_stuff->c;
shared_stuff->c='0';
}
if(newConsum>'0'&&newConsum<='9'){
struct tm *ptm;
long ts;
int y,m,d,h,n,s;
ts = time(NULL);
ptm = localtime(&ts);
printf("消费者 %d %ld:%ld:%ld 消费数据:%c 共享内存%c%c%c\n",(int)getpid(),(long)ptm->tm_hour,(long)ptm->tm_min,(long)ptm->tm_sec,newConsum,shared_stuff->a,shared_stuff->b,shared_stuff->c);
}
/* After the critical section, we call semaphore_v, setting the semaphore available,
before going through the for loop again after a random wait. After the loop, the call
to del_semvalue is made to clean up the code. */
if (!semaphore_v(sem_id)) exit(EXIT_FAILURE);
if (!semaphore_v(sem_idW)) exit(EXIT_FAILURE);
srand((unsigned int)time(NULL));
pause_time = rand() % 5+1;
sleep(pause_time);
}
{
sleep(10);
del_semvalue(sem_idR);
del_semvalue(sem_idW);
del_semvalue(sem_id);
}
if (shmdt(shared_memory) == -1) {//将共享内存从当前进程中分离
fprintf(stderr, "shmdt failed\n");
exit(EXIT_FAILURE);
}
if(shmctl(shmid,IPC_RMID,NULL)==-1){//删除该进程对共享内存的连接(该共享内存一直存在直至最后一个连接断开)
fprintf(stderr,"detach erro");
exit(EXIT_FAILURE);
}
exit(EXIT_SUCCESS);
}
/* semaphore_p changes the semaphore by -1 (waiting). */
int semaphore_p(int sem_id)
{
struct sembuf sem_b;
sem_b.sem_num = 0;
sem_b.sem_op = -1; /* P() */
sem_b.sem_flg = SEM_UNDO;
if (semop(sem_id, &sem_b, 1) == -1) {
fprintf(stderr, "semaphore_p failed\n");
return(0);
}
return(1);
}
/* semaphore_v is similar except for setting the sem_op part of the sembuf structure to 1,
so that the semaphore becomes available. */
int semaphore_v(int sem_id)
{
struct sembuf sem_b;
sem_b.sem_num = 0;
sem_b.sem_op = 1; /* V() */
sem_b.sem_flg = SEM_UNDO;
if (semop(sem_id, &sem_b, 1) == -1) {
fprintf(stderr, "semaphore_v failed\n");
return(0);
}
return(1);
}
/* The function set_semvalue initializes the semaphore using the SETVAL command in a
semctl call. We need to do this before we can use the semaphore. */
int set_semvalue(int sem_id,int v)
{
union semun sem_union;
sem_union.val = v;
if (semctl(sem_id, 0, SETVAL, sem_union) == -1) return(0);
return(1);
}
/* The del_semvalue function has almost the same form, except the call to semctl uses
the command IPC_RMID to remove the semaphore's ID. */
void del_semvalue(int sem_id)
{
union semun sem_union;
if (semctl(sem_id, 0, IPC_RMID, sem_union) == -1)
fprintf(stderr, "Failed to delete semaphore\n");
}
生产者
producer.c
#include <time.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <sys/shm.h>
#include "semun.h"
struct share_memory{
char a;
char b;
char c;
};
int semaphore_p(int sem_id);
int semaphore_v(int sem_id);
int set_semvalue(int sem_id);
void del_semvalue(int sem_id);
int main(int argc, char *argv[])
{
fork();
int i;
int pause_time;
int sem_id,sem_idR,sem_idW;
void * shared_memory=(void *)0;
struct share_memory * shared_stuff;
sem_idR = semget((key_t)12340, 1, 0666 | IPC_CREAT);
sem_idW = semget((key_t)12350, 1, 0666 | IPC_CREAT);
sem_id = semget((key_t)12360, 1, 0666 | IPC_CREAT);
int shmid;
shmid=shmget((key_t)12370,sizeof(struct share_memory),0666|IPC_CREAT);
if(shmid == -1){
fprintf(stderr,"shmget failed\n");
exit(EXIT_FAILURE);
}
shared_memory = shmat(shmid, (void *)0, 0);
if (shared_memory == (void *)-1) {
fprintf(stderr, "shmat failed\n");
exit(EXIT_FAILURE);
}
shared_stuff=(struct share_memory *) shared_memory;
if(shared_stuff==NULL){fprintf(stderr,"shared_stuff==NULL\n");return 1;}
printf("Memory attached at %X %d\n",(int)shared_stuff,sizeof(struct share_memory));
for(i = 0; i < 6; i++) {
if (!semaphore_p(sem_idW)) exit(EXIT_FAILURE);
/*if(shared_stuff->a!='0'&&shared_stuff->b!='0'&&shared_stuff->c!='0'){
pause_time = rand() % 5+1;
sleep(pause_time);
}*/
if (!semaphore_p(sem_id)) exit(EXIT_FAILURE);
// puts("provider enter");
srand((unsigned int)time(NULL)*(i+3));
int tmp=rand()%9+1;
char newProduct;
if(shared_stuff->a=='0'){
shared_stuff->a+=tmp;
newProduct=shared_stuff->a;
}else if(shared_stuff->b=='0'){
shared_stuff->b+=tmp;
newProduct=shared_stuff->b;
}else if(shared_stuff->c=='0'){
shared_stuff->c+=tmp;
newProduct=shared_stuff->c;
}
if(newProduct>'0'&&newProduct<='9'){
struct tm *ptm;
long ts;
int y,m,d,h,n,s;
ts = time(NULL);
ptm = localtime(&ts);
printf("生产者 %d %ld:%ld:%ld 生成数据:%c 共享内存:%c%c%c\n",(int)getpid(),(long)ptm->tm_hour,(long)ptm->tm_min,(long)ptm->tm_sec,newProduct,shared_stuff->a,shared_stuff->b,shared_stuff->c);
fflush(stdout);
}
/* After the critical section, we call semaphore_v, setting the semaphore available,
before going through the for loop again after a random wait. After the loop, the call
to del_semvalue is made to clean up the code. */
if (!semaphore_v(sem_id)) exit(EXIT_FAILURE);
if (!semaphore_v(sem_idR)) exit(EXIT_FAILURE);
//puts("provider leave");
srand((unsigned int)time(NULL)*(i+3));
pause_time = rand() % 5+1;
sleep(pause_time);
}
if(shmdt(shared_stuff) == -1){//将共享内存从当前进程中分离
fprintf(stderr,"shmdt failed \n");
exit(EXIT_FAILURE);
}
if(shmctl(shmid,IPC_RMID,NULL)==-1){//删除该进程对共享内存的连接(该共享内存一直存在直至最后一个连接断开)
fprintf(stderr,"detach erro");
exit(EXIT_FAILURE);
}
exit(EXIT_SUCCESS);
}
/* semaphsrore_p changes the semaphore by -1 (waiting). */
int semaphore_p(int sem_id)
{
struct sembuf sem_b;
sem_b.sem_num = 0;
sem_b.sem_op = -1; /* P() */
sem_b.sem_flg = SEM_UNDO;
if (semop(sem_id, &sem_b, 1) == -1) {
printf("semaphore_p failed\n");
return(0);
}
return(1);
}
/* semaphore_v is similar except for setting the sem_op part of the sembuf structure to 1,
so that the semaphore becomes available. */
int semaphore_v(int sem_id)
{
struct sembuf sem_b;
sem_b.sem_num = 0;
sem_b.sem_op = 1; /* V() */
sem_b.sem_flg = SEM_UNDO;
if (semop(sem_id, &sem_b, 1) == -1) {
printf( "semaphore_v failed\n");
return(0);
}
return(1);
}
/* The function set_semvalue initializes the semaphore using the SETVAL command in a
semctl call. We need to do this before we can use the semaphore. */
int set_semvalue(int sem_id)
{
union semun sem_union;
sem_union.val = 1;
if (semctl(sem_id, 0, SETVAL, sem_union) == -1) return(0);
return(1);
}
/* The del_semvalue function has almost the same form, except the call to semctl uses
the command IPC_RMID to remove the semaphore's ID. */
void del_semvalue(int sem_id)
{
union semun sem_union;
if (semctl(sem_id, 0, IPC_RMID, sem_union) == -1)
fprintf(stderr, "Failed to delete semaphore\n");
}
./consumer.exe &
./producer.exe
查看信号量可用 ipcs -s查看
查看共享内存可用 ipcs -m 查看
执行时出现“段错误,核心已转储”可禅看头文件 sys/shm.h 是否已经包含
执行时,时间函数调用出现问题,查看 头文件time.h是否以及包含