只开了一块缓冲区,所以不需要用mutex互斥信号量对其保护,直接交替PV操作就行了。应当找时间系统学习一下Linux下的C/C++编程,有很多有意思的功能。下面两个程序可以开两个terminal,编译好后,先执行生产者,再在另一个terminal里执行消费者的程序,然后在生产者的程序里输入(生产的)字符串就行了。因为用的是scanf的%s读入字符串,所以中间有空格时会陆续读入,这种时候观察两个进程的状态挺有意思。
生产者程序
#include<stdio.h>
#include<sys/ipc.h>
#include<sys/shm.h>
#include<stdlib.h>
#include<sys/stat.h>
#include<sys/sem.h>
#define SEM_KEY 6666
#define true 1
#define false 0
//共用体
union semun {
int setval;
struct semid_ds *buf;
unsigned short *array;
};
int main(int argc ,char *argv[])
{
int shmid;//信号量的标识码ID,创建失败时为-1
char *addr;//公用缓冲区(共享内存)首地址
//shmget()创建一个共享内存,权限为0666
shmid=shmget(ftok(".",1000),getpagesize(),IPC_CREAT | 0666);
//创建失败时为-1
if(shmid==-1)
{
perror("shmget error:");
exit(EXIT_FAILURE);
}
//输出共享内存的标识码ID
printf("共享内存的标识码ID:=%d\n",shmid);
//获取共享内存的起始地址,且为可读可写
addr=shmat(shmid,0,0);
//获取失败时为-1
if(-1==*addr)
{
perror("shmat error:");
exit(EXIT_FAILURE);
}
/*
int semget(key_t _key,int _nsems,int _semflg);
功能:创建一个新的信号量或获取一个已经存在的信号量的键值。
*/
//创建2个信号量的信号集
int semid=semget(SEM_KEY, 2, IPC_CREAT | 0600);
//创建失败时为-1
if (-1 == semid)
{
perror("semget");
exit(EXIT_FAILURE);
}
printf("信号集的标识码ID:=%d\n", semid);
//初始化信号集
union semun sem_args;
unsigned short array[2]={0,1};
sem_args.array = array;//赋值给共用体
//SETALL代表设置信号集中所有的信号量的值。1,代表2个,sem_args是具体初始化的值放在共用体中
int ret=semctl(semid, 1, SETALL, sem_args);
//设置失败时为-1
if (-1 == ret)
{
perror("semctl");
exit(EXIT_FAILURE);
}
//对资源的使用处理操作
/*
struct sembuf{
unsigned short sem_num;//第几个信号量,第一个信号量为0
short sem_op;//对该信号量的操作(+/-)
short sem_flg;//常被设置为SEM_UNDO,使得如果一个进程在没有释放信号量的情况下结束了执行,该进程掌握的信号量由系统自己释放
};
*/
struct sembuf P_full={0, -1, SEM_UNDO};
struct sembuf V_full={0, +1, SEM_UNDO};
struct sembuf P_empty={1, -1, SEM_UNDO};
struct sembuf V_empty={1, +1, SEM_UNDO};
printf("<这里是生产者进程,在这里输入生产出的字符串>\n");
//不断生产
while(true)
{
/*
int semop(int semid ,struct sembuf *_sops ,size_t _nsops);
功能:用户改变信号量的值。也就是使用资源还是释放资源使用权
*/
semop(semid, &P_empty, 1);//P(empty)等待至空缓冲区数量不为0
scanf("%s",addr);
printf(">%s被读入了缓冲区\n",addr);
semop(semid, &V_full, 1);//V(full)满缓冲区数量+1
if(addr[0]=='@')
break;//跳出循环,准备结束程序
}
return 0;
}
消费者程序
#include<stdio.h>
#include<sys/ipc.h>
#include<sys/shm.h>
#include<stdlib.h>
#include<sys/sem.h>
#include<string.h>
#include<sys/stat.h>
#define SEM_KEY 6666
#define true 1
#define false 0
//共用体
union semun {
int setval;
struct semid_ds *buf;
unsigned short *array;
};
int main(int argc ,char *argv[])
{
int shmid;//信号量的标识码ID,创建失败时为-1
char *addr;//公用缓冲区首地址
//打开刚才原程序1创建的共享内存,权限为0666
shmid= shmget(ftok(".",1000),getpagesize(), IPC_CREAT | 0666);
printf("共享内存的标识码ID:=%d\n",shmid);
//创建失败时为-1
if(shmid==-1)
{
perror("shmget error:");
exit(EXIT_FAILURE);
}
addr=shmat(shmid,0,0);//获取共享内存的起始地址,且为可读可写
if(-1==*addr)
{
perror("shmat error:");
exit(EXIT_FAILURE);
}
int semid=semget(SEM_KEY, 0, IPC_CREAT | 0600);//取得信号量
if(-1==semid)
{
perror("semget");
exit(EXIT_FAILURE);
}
printf("信号集的标识码ID:=%d\n", semid);
//对资源的使用处理操作
struct sembuf P_full={0, -1, SEM_UNDO};
struct sembuf V_full={0, +1, SEM_UNDO};
struct sembuf P_empty={1, -1, SEM_UNDO};
struct sembuf V_empty={1, +1, SEM_UNDO};
printf("<这里是消费者进程,不要在这里输入任何东西>\n");
//不断消费
while(true)
{
semop(semid, &P_full, 1);//P(full)等待至满缓冲区数量不为0
//如果检测到生产者输入的第一个字符是@
if(addr[0]=='@')
break;//跳出循环,准备结束程序
printf("消费ing...\n");
sleep(3);//消费需要时间
printf(">消费了");
puts(addr);//输出这次消费的东西
semop(semid, &V_empty, 1);//V(empty)空缓冲区数量+1
}
if(-1==semctl(semid,1,IPC_RMID,0))//删除信号量
{
perror("semctl error:");
exit(EXIT_FAILURE);
}
if(-1==shmdt(addr))//释放共享内存,使其不再有任何指向它的指针
{
perror("shmdt error:");
exit(EXIT_FAILURE);
}
if (shmctl(shmid,IPC_RMID,0)==-1)//删除共享内存
{
perror("shctl error:");
exit(EXIT_FAILURE);
}
return 0;
}
输入@符号就可以结束两个进程了,下面是运行的结果。
在做和进程有关的编程学习时,sleep()函数真的是一个非常好用的便于观察和思考的方法。