#include <stdio.h>
#include <time.h>
#include <unistd.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/sem.h>
#define NEED_P 2 //生產者進程數
#define NEED_C 2 //消費者進程數
#define WORKS_P 10 //每個生產者進程執行的次數
#define WORKS_C 10 //每個消費者進程執行的次數
#define BUF_LENGTH (sizeof(struct mybuffer)) //共享內存的大小
#define BUF_NUM 10 //緩衝區的大小
#define SHM_MODE 0600
#define SEM_ALL_KEY 2411
#define SEM_EMPTY 0 //空的信號量
#define SEM_FULL 1 //滿的信號量
//緩衝區結構(循環隊列)
struct mybuffer
{
char letter[BUF_NUM];
int head;
int tail;
int is_empty;
};
//得到5以內的一個隨機數
int get_random()
{
int t;
srand((unsigned)(getpid() + time(NULL)));
t = rand() % 5;
return t;
}
//得到A~Z的一個隨機字母
char get_char()
{
char a;
srand((unsigned)(getpid() + time(NULL)));
a = (char)((char)(rand() % 26) + 'A');
return a;
}
//P操作
void p(int sem_id, int sem_num)
{
struct sembuf pc;
pc.sem_num = sem_num; //信號量的類型
pc.sem_op = -1; //操作類型
pc.sem_flg = 0; //操作的標誌
semop(sem_id, &pc, 1);
}
//V操作
void v(int sem_id, int sem_num)
{
struct sembuf pc;
pc.sem_num = sem_num;
pc.sem_op = 1;
pc.sem_flg = 0;
semop(sem_id, &pc, 1);
}
//主函數
int main(int argc, char * argv[])
{
int i, j;
int shm_id, sem_id;
int num_p = 0, num_c = 0;
struct mybuffer * shmptr;
char lt;
time_t now;
pid_t pid_p, pid_c;
sem_id = semget(SEM_ALL_KEY, 2, IPC_CREAT | 0600); //建立信號量,建立的信號量數量爲2,返回此信號量的ID
if (sem_id >= 0)
{
printf("Main process starts. Semaphore created.\n");
}
semctl(sem_id, SEM_EMPTY, SETVAL, BUF_NUM); //初始設置第一個信號量SE_EMPTY的值爲BUF_NUM;
semctl(sem_id, SEM_FULL, SETVAL, 0); //初始設置第二個信號量SE_FULL的值爲0;
if ((shm_id = shmget(IPC_PRIVATE, BUF_LENGTH, SHM_MODE)) < 0) //分配內存共享
{
printf("Error on shmget.\n");
exit(1);
}
if ((shmptr = shmat(shm_id, 0, 0)) == (void *)-1) //將共享的內存附加到進程的地址空間
{
printf("Error on shmat.\n");
exit(1);
}
shmptr->head = 0;
shmptr->tail = 0;
shmptr->is_empty = 1;
while ((num_p++) < NEED_P)
{
if ((pid_p = fork()) < 0) //創建新的進程
{
printf("Error on fork.\n");
exit(1);
}
//如果是子進程,開始創建生產者
if (pid_p == 0)
{
if ((shmptr = shmat(shm_id, 0, 0)) == (void *)-1)
{
printf("Error on shmat.\n");
exit(1);
}
for (i = 0; i < WORKS_P; i++)
{
p(sem_id, SEM_EMPTY);
sleep(get_random());
shmptr->letter[shmptr->tail] = lt = get_char();
shmptr->tail = (shmptr->tail + 1) % BUF_NUM;
shmptr->is_empty = 0;
now = time(NULL);
printf("%02d:%02d:%02d\t", localtime(&now)->tm_hour, localtime(&now)->tm_min, localtime(&now)->tm_sec);
for (j = (shmptr->tail - 1 >= shmptr->head) ? (shmptr->tail - 1) : (shmptr->tail - 1 + BUF_NUM); !(shmptr->is_empty) && j >= shmptr->head; j--)
{
printf("%c", shmptr->letter[j % BUF_NUM]);
}
printf("\tProducer %d puts '%c'.\n", num_p, lt);
fflush(stdout); //刷新緩衝區
v(sem_id, SEM_FULL);
}
shmdt(shmptr); //分離共享內存
exit(0);
}
}
while (num_c++ < NEED_C)
{
if ((pid_c = fork()) < 0) //創建新的進程
{
printf("Error on fork.\n");
exit(1);
}
//如果是子進程,開始創建消費者
if (pid_c == 0)
{
if ((shmptr = shmat(shm_id, 0, 0)) == (void *)-1)
{
printf("Error on shmat.\n");
exit(1);
}
for (i = 0; i < WORKS_C; i++)
{
p(sem_id, SEM_FULL);
sleep(get_random());
lt = shmptr->letter[shmptr->head];
shmptr->head = (shmptr->head + 1) % BUF_NUM;
shmptr->is_empty = (shmptr->head == shmptr->tail);
now = time(NULL);
printf("%02d:%02d:%02d\t", localtime(&now)->tm_hour, localtime(&now)->tm_min, localtime(&now)->tm_sec);
for (j = (shmptr->tail - 1 >= shmptr->head) ? (shmptr->tail - 1) : (shmptr->tail - 1 + BUF_NUM); !(shmptr->is_empty) && j >= shmptr->head; j--)
{
printf("%c", shmptr->letter[j % BUF_NUM]);
}
printf("\tConsumer %d gets '%c'.\n", num_c, lt);
fflush(stdout);
v(sem_id, SEM_EMPTY);
}
shmdt(shmptr);
exit(0);
}
}
//主控程序最後退出
while(wait(0) != -1);
shmdt(shmptr); //分離共享內存
shmctl(shm_id, IPC_RMID, 0); //撤銷共享內存
semctl(sem_id, IPC_RMID, 0); //釋放內存
printf("Main process ends.\n");
fflush(stdout);
exit(0);
}