本示例主要体现进程间的直接制约关系,由于使用共享存储区,也存在间接制约关系。进程分为服务进程和客户进程,服务进程只有一个,作为消费者,在每次客户进程改变共享存储区内容时显示其数值。各客户进程作为生产者,如果共享存储区内容已经显示(被消费),可以接收用户从键盘输入的整数,放在共享存储区。
编译后执行,第一个进程实例将作为服务进程,提示:
ACT CONSUMER!!! To end, try Ctrl+C or use kill.
服务进程一直循环执行,直到用户按Ctrl+C终止执行,或使用kill命令杀死服务进程。
其他进程实例作为客户进程,提示:
Act as producer. To end, input 0 when prompted.
客户进程一直循环执行,直到用户输入0。
#include
#include
#include
#include
#include
#include
#include
#include
#define MY_SHMKEY 10071500 // need to change
#define MY_SEMKEY 10071500 // need to change
void sigend(int);
int shmid, semid;
int main(void)
{
int *shmptr, semval, local;
struct sembuf semopbuf;
if((shmid=shmget(MY_SHMKEY, sizeof(int), IPC_CREAT|IPC_EXCL|0666)) < 0)
{ /* shared memory exists, act as client */
shmid=shmget(MY_SHMKEY, sizeof(int), 0666);
semid=semget(MY_SEMKEY, 2, 0666);
shmptr=(int *)shmat(shmid, 0, 0);
printf("Act as producer. To end, input 0 when prompted.\n\n");
printf("Input a number:\n");
scanf("%d", &local);
while( local )
{
semopbuf.sem_num=0;
semopbuf.sem_op=-1;
semopbuf.sem_flg=SEM_UNDO;
semop(semid, &semopbuf, 1); /* P(S1) */
*shmptr = local;
semopbuf.sem_num=1;
semopbuf.sem_op=1;
semopbuf.sem_flg=SEM_UNDO;
semop(semid, &semopbuf, 1); /* V(S2) */
printf("Input a number:\n");
scanf("%d", &local);
}
}
else /* acts as server */
{
semid=semget(MY_SEMKEY, 2, IPC_CREAT|0666);
shmptr=(int *)shmat(shmid, 0, 0);
semval=1;
semctl(semid, 0, SETVAL, semval); /* set S1=1 */
semval=0;
semctl(semid, 1, SETVAL, semval); /* set S2=0 */
signal(SIGINT, sigend);
signal(SIGTERM, sigend);
printf("ACT CONSUMER!!! To end, try Ctrl+C or use kill.\n\n");
while(1)
{
semopbuf.sem_num=1;
semopbuf.sem_op=-1;
semopbuf.sem_flg=SEM_UNDO;
semop(semid, &semopbuf, 1); /* P(S2) */
printf("Shared memory set to %d\n", *shmptr);
semopbuf.sem_num=0;
semopbuf.sem_op=1;
semopbuf.sem_flg=SEM_UNDO;
semop(semid, &semopbuf, 1); /* V(S1) */
}
}
}
void sigend(int sig)
{
shmctl(shmid, IPC_RMID, 0);
semctl(semid, IPC_RMID, 0);
exit(0);
}
结果分析:
第一次执行程序,创建公共存储区成功。If条件不成立,进入else分支,创建了一个服务进程:消费者。因公共存储区为空,故消费者进程处于等待状态。
第二次执行该程序,创建公共存储区失败,条件成立,进入if分支,即:创建一个客户进程:生产者。生产者提示输入一个整数,输入后,生产者将该整数保存到公共存储区,此时消费者会唤醒进行消费,把数据读出并输出。
改造该程序,取消所有的同步机制:
即去掉实现wait(full) signal(empty) 和 wait(empty) signal(full)的语句semop(semid, &semopbuf, 1);和semop(semid, &semopbuf, 1);
并在消费者进程中添加语句sleep(5),使错误易于观察
#include
#include
#include
#include
#include
#include
#include
#include
#define MY_SHMKEY 10071500 // need to change
#define MY_SEMKEY 10071500 // need to change
void sigend(int);
int shmid, semid;
int main(void)
{
int *shmptr, semval, local;
struct sembuf semopbuf;
if((shmid=shmget(MY_SHMKEY, sizeof(int), IPC_CREAT|IPC_EXCL|0666)) < 0)
{ /* shared memory exists, act as client */
shmid=shmget(MY_SHMKEY, sizeof(int), 0666);
semid=semget(MY_SEMKEY, 2, 0666);
shmptr=(int *)shmat(shmid, 0, 0);
printf("Act as producer. To end, input 0 when prompted.\n\n");
printf("Input a number:\n");
scanf("%d", &local);
while( local )
{
semopbuf.sem_num=0;
semopbuf.sem_op=-1;
semopbuf.sem_flg=SEM_UNDO;
//semop(semid, &semopbuf, 1); /* P(S1) */
*shmptr = local;
semopbuf.sem_num=1;
semopbuf.sem_op=1;
semopbuf.sem_flg=SEM_UNDO;
//semop(semid, &semopbuf, 1); /* V(S2) */
printf("Input a number:\n");
scanf("%d", &local);
}
}
else /* acts as server */
{
semid=semget(MY_SEMKEY, 2, IPC_CREAT|0666);
shmptr=(int *)shmat(shmid, 0, 0);
semval=1;
semctl(semid, 0, SETVAL, semval); /* set S1=1 */
semval=0;
semctl(semid, 1, SETVAL, semval); /* set S2=0 */
signal(SIGINT, sigend);
signal(SIGTERM, sigend);
printf("ACT CONSUMER!!! To end, try Ctrl+C or use kill.\n\n");
while(1)
{
semopbuf.sem_num=1;
semopbuf.sem_op=-1;
semopbuf.sem_flg=SEM_UNDO;
sleep(5);
//semop(semid, &semopbuf, 1); /* P(S2) */
printf("Shared memory set to %d\n", *shmptr);
semopbuf.sem_num=0;
semopbuf.sem_op=1;
semopbuf.sem_flg=SEM_UNDO;
//semop(semid, &semopbuf, 1); /* V(S1) */
}
}
}
void sigend(int sig)
{
shmctl(shmid, IPC_RMID, 0);
semctl(semid, IPC_RMID, 0);
exit(0);
}
结果分析
第一次执行程序,可以看到刚开始输出“Shared memory set to 0”;第二次执行程序,生产者依次生产1,2,3,4,5,由于sleep(5),消费者只消费了“5”,而且多次消费。即,生产者没生产时,消费者已经开始消费了。并且生产者生产一个,而消费者却消费多次。故,程序出现错误。