|  | 
 
| 8.7  实验内容 
 8.7.1  管道通信实验
 
 1.实验目的
 
 通过编写有名管道多路通信实验,读者可进一步掌握管道的创建、读写等操作,同时,也复习使用select()函数实现管道的通信。
 
 2.实验内容
 
 读者还记得在6.3.3小节中,通过mknod命令创建两个管道的实例吗?本实例只是在它的基础上添加有名管道的创建,而不用再输入mknod命令。
 
 3.实验步骤
 
 (1)画出流程图。
 该实验流程图如图8.9所示。
 
 
         图8.9  8.6.1实验流程图 
 (2)编写代码。
 该实验源代码如下所示。
 
 /* pipe_select.c*/
 #include <fcntl.h>
 #include <stdio.h>
 #include <unistd.h>
 #include <stdlib.h>
 #include <string.h>
 #include <time.h>
 #include <errno.h>
 
 #define FIFO1               "in1"
 #define FIFO2               "in2"
 #define MAX_BUFFER_SIZE         1024          /* 缓冲区大小*/
 #define IN_FILES            3                   /* 多路复用输入文件数目*/
 #define TIME_DELAY          60              /* 超时值秒数 */
 #define MAX(a, b)           ((a > b)?(a):(b))
 
 int main(void)
 {
 int fds[IN_FILES];
 char buf[MAX_BUFFER_SIZE];
 int i, res, real_read, maxfd;
 struct timeval tv;
 fd_set inset,tmp_inset;
 
 fds[0] = 0;
 
 /* 创建两个有名管道 */
 if (access(FIFO1, F_OK) == -1)
 {
 if ((mkfifo(FIFO1, 0666) < 0) && (errno != EEXIST))
 {
 printf("Cannot create fifo file\n");
 exit(1);
 }
 }
 if (access(FIFO2, F_OK) == -1)
 {
 if ((mkfifo(FIFO2, 0666) < 0) && (errno != EEXIST))
 {
 printf("Cannot create fifo file\n");
 exit(1);
 }
 }
 
 /* 以只读非阻塞方式打开两个管道文件 */
 if((fds[1] = open (FIFO1, O_RDONLY|O_NONBLOCK)) < 0)
 {
 printf("Open in1 error\n");
 return 1;
 }
 if((fds[2] = open (FIFO2, O_RDONLY|O_NONBLOCK)) < 0)
 {
 printf("Open in2 error\n");
 return 1;
 }
 
 /*取出两个文件描述符中的较大者*/
 maxfd = MAX(MAX(fds[0], fds[1]), fds[2]);
 /*初始化读集合inset,并在读文件描述符集合中加入相应的描述集*/
 FD_ZERO(&inset);
 for (i = 0; i < IN_FILES; i++)
 {
 FD_SET(fds, &inset);
 }
 FD_SET(0, &inset);
 
 tv.tv_sec = TIME_DELAY;
 tv.tv_usec = 0;
 /*循环测试该文件描述符是否准备就绪,并调用select()函数对相关文件描述符做相应操作*/
 while(FD_ISSET(fds[0],&inset)
 || FD_ISSET(fds[1],&inset) || FD_ISSET(fds[2], &inset))
 {
 /* 文件描述符集合的备份, 免得每次进行初始化 */
 tmp_inset = inset;
 res = select(maxfd + 1, &tmp_inset, NULL, NULL, &tv);
 switch(res)
 {
 case -1:
 {
 printf("Select error\n");
 return 1;
 }
 break;
 case 0: /* Timeout */
 {
 printf("Time out\n");
 return 1;
 }
 break;
 default:
 {
 for (i = 0; i < IN_FILES; i++)
 {
 if (FD_ISSET(fds, &tmp_inset))
 {
 memset(buf, 0, MAX_BUFFER_SIZE);
 real_read = read(fds, buf, MAX_BUFFER_SIZE);
 if (real_read < 0)
 {
 if (errno != EAGAIN)
 {
 return 1;
 }
 }
 else if (!real_read)
 {
 close(fds);
 FD_CLR(fds, &inset);
 }
 else
 {
 if (i == 0)
 {/* 主程序终端控制 */
 if ((buf[0] == 'q') || (buf[0] == 'Q'))
 {
 return 1;
 }
 }
 else
 {/* 显示管道输入字符串 */
 buf[real_read] = '\0';
 printf("%s", buf);
 }
 }
 } /* end of if */
 } /* end of for */
 }
 break;
 } /* end of switch */
 } /*end of while */
 return 0;
 }
 
 (3)编译并运行该程序。
 
 (4)另外打开两个虚拟终端,分别键入“cat > in1”和“cat > in2”,接着在该管道中键入相关内容,并观察实验结果。
 
 4.实验结果
 
 实验运行结果与第6章的例子完全相同。
 
 $ ./pipe_select (必须先运行主程序)
 SELECT CALL
 select call
 TEST PROGRAMME
 test programme
 END
 end
 q /* 在终端上输入’q’或’Q’立刻结束程序运行 */
 
 $ cat > in1
 SELECT CALL
 TEST PROGRAMME
 END
 
 $ cat > in2
 select call
 test programme
 end
 
 8.7.2  共享内存实验
 
 1.实验目的
 
 通过编写共享内存实验,读者可以进一步了解使用共享内存的具体步骤,同时也进一步加深对共享内存的理解。在本实验中,采用信号量作为同步机制完善两个进程(“生产者”和“消费者”)之间的通信。其功能类似于“消息队列”中的实例,详见8.5.2小节。在实例中使用的与信号量相关的函数,详见8.3.3小节。
 
 2.实验内容
 
 该实现要求利用共享内存实现文件的打开和读写操作。
 
 3.实验步骤
 
 (1)画出流程图。
 该实验流程图如图8.10所示。
 
 
         图8.10  实验8.6.2流程图 
 (2)编写代码。
 下面是共享内存缓冲区的数据结构的定义。
 
 /* shm_com.h */
 #include <unistd.h>
 #include <stdlib.h>
 #include <stdio.h>
 #include <string.h>
 #include <sys/types.h>
 #include <sys/ipc.h>
 #include <sys/shm.h>
 #define SHM_BUFF_SZ 2048
 struct shm_buff
 {
 int pid;
 char buffer[SHM_BUFF_SZ];
 };
 
 以下是“生产者”程序部分。
 
 /* sem_com.h 和 sem_com.c 与“信号量”小节示例中的同名程序相同 */
 /* producer.c */
 #include "shm_com.h"
 #include "sem_com.h"
 #include <signal.h>
 int ignore_signal(void)
 { /* 忽略一些信号,免得非法退出程序 */
 signal(SIGINT, SIG_IGN);
 signal(SIGSTOP, SIG_IGN);
 signal(SIGQUIT, SIG_IGN);
 return 0;
 }
 
 int main()
 {
 void *shared_memory = NULL;
 struct shm_buff *shm_buff_inst;
 char buffer[BUFSIZ];
 int shmid, semid;
 /* 定义信号量,用于实现访问共享内存的进程之间的互斥*/
 ignore_signal(); /* 防止程序非正常退出 */
 semid = semget(ftok(".", 'a'),  1, 0666|IPC_CREAT); /* 创建一个信号量*/
 init_sem(semid);/* 初始值为1 */
 
 /* 创建共享内存 */
 shmid = shmget(ftok(".", 'b'), sizeof(struct shm_buff), 0666|IPC_CREAT);
 if (shmid == -1)
 {
 perror("shmget failed");
 del_sem(semid);
 exit(1);
 }
 
 
 /* 将共享内存地址映射到当前进程地址空间 */
 shared_memory = shmat(shmid, (void*)0, 0);
 if (shared_memory == (void*)-1)
 {
 perror("shmat");
 del_sem(semid);
 exit(1);
 }
 printf("Memory attached at %X\n", (int)shared_memory);
 /* 获得共享内存的映射地址 */
 shm_buff_inst = (struct shared_use_st *)shared_memory;
 do
 {
 sem_p(semid);
 printf("Enter some text to the shared memory(enter 'quit' to exit):");
 /* 向共享内存写入数据 */
 if (fgets(shm_buff_inst->buffer, SHM_BUFF_SZ, stdin) == NULL)
 {
 perror("fgets");
 sem_v(semid);
 break;
 }
 shm_buff_inst->pid = getpid();
 sem_v(semid);
 } while(strncmp(shm_buff_inst->buffer, "quit", 4) != 0);
 
 /* 删除信号量 */
 del_sem(semid);
 /* 删除共享内存到当前进程地址空间中的映射 */
 if (shmdt(shared_memory) == 1)
 {
 perror("shmdt");
 exit(1);
 }
 exit(0);
 }
 
 以下是“消费者”程序部分。
 
 /* customer.c */
 #include "shm_com.h"
 #include "sem_com.h"
 
 int main()
 {
 void *shared_memory = NULL;
 struct shm_buff *shm_buff_inst;
 int shmid, semid;
 /* 获得信号量 */
 semid = semget(ftok(".", 'a'),  1, 0666);
 if (semid == -1)
 {
 perror("Producer is'nt exist");
 exit(1);
 }
 /* 获得共享内存 */
 shmid = shmget(ftok(".", 'b'), sizeof(struct shm_buff), 0666|IPC_CREAT);
 if (shmid == -1)
 {
 perror("shmget");
 exit(1);
 }
 /* 将共享内存地址映射到当前进程地址空间 */
 shared_memory = shmat(shmid, (void*)0, 0);
 if (shared_memory == (void*)-1)
 {
 perror("shmat");
 exit(1);
 }
 printf("Memory attached at %X\n", (int)shared_memory);
 /* 获得共享内存的映射地址 */
 shm_buff_inst = (struct shm_buff *)shared_memory;
 do
 {
 sem_p(semid);
 printf("Shared memory was written by process %d :%s"
 ,                         shm_buff_inst->pid, shm_buff_inst->buffer);
 if (strncmp(shm_buff_inst->buffer, "quit", 4) == 0)
 {
 break;
 }
 shm_buff_inst->pid = 0;
 memset(shm_buff_inst->buffer, 0, SHM_BUFF_SZ);
 sem_v(semid);
 } while(1);
 
 /* 删除共享内存到当前进程地址空间中的映射 */
 if (shmdt(shared_memory) == -1)
 {
 perror("shmdt");
 exit(1);
 }
 /* 删除共享内存 */
 if (shmctl(shmid, IPC_RMID, NULL) == -1)
 {
 perror("shmctl(IPC_RMID)");
 exit(1);
 }
 exit(0);
 }
 
 4.实验结果
 
 $./producer
 Memory attached at B7F90000
 Enter some text to the shared memory(enter 'quit' to exit):First message
 Enter some text to the shared memory(enter 'quit' to exit):Second message
 Enter some text to the shared memory(enter 'quit' to exit):quit
 $./customer
 Memory attached at B7FAF000
 Shared memory was written by process 3815 :First message
 Shared memory was written by process 3815 :Second message
 Shared memory was written by process 3815 :quit
 | 
 |