项目描述:# a+ N# g. _$ c$ F9 V2 b) B! ]
有4个进程,A进程和B进程负责通信,从标准输入读到的字符串通过管道发给对方,A1和B1进程负责显示,其中:& m1 n! O$ h% c% f+ O' O2 b
- A进程和B进程通过管道通信,A进程和A1进程通过共享内存通信,B进程和B1进程通过消息队列通信;
- A进程从标准输入读到的字符串后,放到管道和共享内存里,从管道中读到的字符串放到共享内存里,B进程从管道中拿到A放的字符串,A1进程到共享内存中拿到字符串,打印到屏幕上;
- B进程从标准输入读到的字符串发给A进程,同时通过消息队列发给B1进程,B1进程从消息队列中读出消息,打印到屏幕上;
- 退出时,在A和B任意一个进程中输入 Crtl+c 或者 Ctrl+\ ,四个进程会删除所有管道、共享内存、消息队列等资源,然后有序退出。7 z w& b% X: f# X
操作系统:Ubuntu20.4; @/ x6 k! @3 R. L1 n- x6 T t. I
语言:c
x7 N& ? z2 {: B4 ~. c/ G9 p& Y9 ^" t编译器:gcc
, d) K6 e2 a s) M- gfunc.h% U" f5 u8 l5 [* G
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- #include <sys/types.h>
- #include <unistd.h>
- #include <time.h>
- #include <sys/mman.h>
- #include <fcntl.h>
- #include <sys/select.h>
- #include <sys/shm.h>
- #include <sys/sem.h>
- #include <sys/msg.h>
- #include <signal.h>
-
- #define ARGS_CHECK(argc, num){if(argc!=num){fprintf(stderr,"args error!\n"); return -1;}}
-
- #define ERROR_CHECK(ret,num,msg){if(ret == num){perror(msg); return -1;}}
a.c
; J" _* r1 r/ [9 i" K- //========= A窗口 ===========
- //1.从标准输入读取数据,通过有名管道发送给B窗口
- //2.接收从B窗口发送过来的数据
- //3.通过共享内存和信号量,将从B来的数据发送给A1
- //===========================
-
- #include <func.h>
-
- int chatA(int shmid, int semid, char *p);
- int sndToA1(int semid, int shmid, char *p, char *msg);//把要打印的消息发送给A1
- void closeAll();//把关闭消息发送出去,关闭共享内存和信号量集
- void sigFunc(int signum);//新的2号和3号信号处理函数,如果在A窗口发生2号和3号信号就调用close函数
-
- //全局变量,后面捕获到退出信号回收资源时使用
- int semid;
- int shmid;//共享内存
- char *p;
-
- int fdWrite;
- int fdRead;
-
- int main()
- {
- //1.创建信号量集,如果有新消息就往共享内存中写,类似生产者
- semid = semget(2000, 1, IPC_CREAT|0666);
- ERROR_CHECK(semid, -1, "A semget");
- shmid = shmget(1000, 4096, IPC_CREAT|0666);//创建一个共享内存
- ERROR_CHECK(shmid, -1, "shmget");
- p = (char *)shmat(shmid, NULL, 0);
-
- signal(SIGINT, sigFunc);
- signal(SIGQUIT, sigFunc);
-
- int ret = chatA(shmid, semid, p);
- ERROR_CHECK(ret, -1, "run A");//检查是否成功打开通信窗口
- return 0;
- }
-
- int chatA(int shmid, int semid, char *p){
- //成功运行返回1,否则返回-1
- fdRead = open("1.pipe", O_RDONLY);//以只读模式打开管道1
- ERROR_CHECK(fdRead, -1, "open fdRead");//检查是否成功打开
- fdWrite = open("2.pipe", O_WRONLY);//以只写模式打开管道2
- ERROR_CHECK(fdWrite, -1, "open fdWrite");//检查
- setbuf(stdin, NULL);
- puts("=========== A ===========");
- char buf[512] = {0};
- fd_set rdset;//设置一个信箱,用来监控有没有读取到信息
- while(1){
- struct timeval timeout;//设置超时
- timeout.tv_sec = 5;
- timeout.tv_usec = 15000000;//超过5秒没有接收到信息就是超时
- FD_ZERO(&rdset);//初始化集合,清空信箱
- FD_SET(fdRead, &rdset);//将要监听的管道1注册到集合中
- FD_SET(STDIN_FILENO, &rdset);//将要监听的标准输入注册到集合中
- int tret = select(fdRead + 1,&rdset,NULL,NULL,&timeout);//调用select进行监听
- if(tret == 0){
- puts("time out!");
- }
- //select阻塞进程,任意一个FD就绪,解除阻塞
- //解除阻塞,检查是谁就绪
- if(FD_ISSET(fdRead, &rdset)){
- //如果是管道就绪,读取管道中的内容,发送给A1
- memset(buf, 0, sizeof(buf));//清空buf中的内容,用来接收管道中的信息
- int ret = read(fdRead, buf, 1024);//将管道中的信息读取出来
- if(ret == 0){
- //如果另一端对管道的写先关闭了,退出聊天
- sigFunc(2);
- break;
- }
- //获取从B来的消息的类型
- int type = 0;
- sscanf(buf, "%*d %d", &type);//读取消息的类别,1类为正常,2类为关闭所有窗口
- int snd_ret = 0;
-
- switch (type){
- case 1:
- //如果是1号信息,通过共享内存直接把消息发送给A1
- snd_ret = sndToA1(shmid, semid, p, buf);
- ERROR_CHECK(snd_ret, -1, "sndToA1");
- break;
- case 2:
- //=====如果是从B发过来的2号信息,关闭所有窗口=====
- //向A1发送一个空的2号信号,让A1自己退出,然后自己再退出
- sigFunc(2);
- exit(0);
- }
- }
- if(FD_ISSET(STDIN_FILENO, &rdset)){
- //如果标准输入准备就绪,读取标准输入区的数据,标记为3号信号,发送给A1和B
- time_t localtm;
- time(&localtm);//获取当前时间
- localtm += 8*3600;
- memset(buf, 0, sizeof(buf));//清空buf
- int ret = read(STDIN_FILENO, buf, 1024);//读取数据
- if(ret == 0){
- //如果在标准输入中读到了终止符,退出聊天窗口
- puts("I quite.");
- break;
- }
- char sstoA1[1024] = {0};//用来拼接数据,发送给A1的数据
- char sstoB[1024] = {0};//用来拼接数据,发送给B的数据
- sprintf(sstoA1, "%ld %d %s", localtm, 3, buf); //标注为三号信号发送给A1
- sprintf(sstoB, "%ld %d %s", localtm, 1, buf); //标注为1号信号发送给B
- sndToA1(shmid, semid, p, sstoA1);//发送给A1
- write(fdWrite, sstoB, sizeof(sstoB));//通过管道发送给B
- }
- }
- close(fdRead);
- close(fdWrite);
- return 1;//程序成功运行结束,返回1
- }
-
- int sndToA1(int shmid, int semid, char *p, char *msg){
- //使用共享内存和信号量给A1传递信息
- //信号量集的操作,如果有新消息就往共享内存中写,类似生产者
- struct sembuf V;
- V.sem_num = 0;
- V.sem_op = +1;
|