现在Linux使用的进程间通信方式包括:
1、管道(pipe)和有名管道(FIFO)
2、信号(signal)
3、消息队列
4、共享内存
5、信号量
6、套接字(socket)
管道通信
管道是单向的、先进先出的,它把一个进程的输出和另一个进程的输入连接在一起。 一个进程(写进程)在管道的尾部写入数据,另一个进程(读进程)从管道的头部读出数据。
管道提供了简单的流控制机制,进程试图读空管道时,进程将阻塞。同样,管道已经满时,进程再试图向管道写入数据,进程将阻塞
管道包括无名管道和有名管道两种,前者用于父进程和子进程间的通信,后者可用于运行于同一系统中的任意两个进程间的通信。
无名管道创建: int pipe(int filedis[2]);
当一个管道建立时,它会创建两个文件描述符: filedis[0] 用于读管道、 filedis[1] 用于写管道
管道的创建: #include <unistd.h> #include <errno.h> #include <stdio.h> #include <stdlib.h> int main() { int pipe_fd[2]; if(pipe(pipe_fd)<0) { printf("pipe create error\n"); return -1; } else printf("pipe create success\n"); close(pipe_fd[0]); close(pipe_fd[1]); } 重要注意点!:必须在系统调用fork( )前调用pipe( ),否则子进程将不会继承文件描述符
信号通信
常见的几种信号:
§ SIGHUP: 从终端上发出的结束信号
§ SIGINT: 来自键盘的中断信号(Ctrl-C)
§ SIGKILL:该信号结束接收信号的进程
§ SIGTERM:kill 命令发出的信号
§ SIGCHLD:标识子进程停止或结束的信号
§ SIGSTOP:来自键盘(Ctrl-Z)或调试程序的停止执行信号
发送信号的主要函数有 kill和raise。
区别: Kill既可以向自身发送信号,也可以向其他进程发送信号。与kill函数不同的是,raise函数是向进程自身发送信号
int kill(pid_t pid, int signo) int raise(int signo)
共享内存
共享内存是被多个进程共享的一部分物理内存.共享内存是进程间共享数据的一种最快的方法,一个进程向共享内存区域写入了数据,共享这个内存区域的所有进程就可以立刻看到其中的内容。
共享内存实现分为两个步骤:
一、创建共享内存,使用shmget函数 int shmget ( key_t key, int size, int shmflg )
二、映射共享内存,将这段创建的共享内存映射到具体的进程空间去,使用shmat函数:
char * shmat ( int shmid, char *shmaddr, int flag)
关于内存共享的一段源代码 :
功能:两个子程序,若只打开A,则输出A的1-99,若打开A后,打开B,则A从计数到X(取决于打开的时间),A与B就共享内存,互相读取数据,输入总计为1-99。
/************************************************************** > File Name:send.c > Author: wow66lfy > Created Time: 2018年08月17日 星期五 11时53分27秒 **************************************************************/ #include <stdio.h> #include <stdlib.h> #include <sys/types.h> #include <sys/shm.h> #include <sys/ipc.h> #include <sys/sem.h> #define SHMKEY 1234 #define SHMSIZE 4096 #define SEMKEY 1234 union semun { int val; struct semid_ds *buf; unsigned short *array; struct seminfo *_buf; }; void sem_p(int semid) { int ret; struct sembuf sbuf; sbuf.sem_num = 0; sbuf.sem_op = -1; sbuf.sem_flg = SEM_UNDO; ret = semop(semid, &sbuf, 1); if(-1 == ret) { perror("semop"); return; } } void sem_v(int semid) { int ret; struct sembuf sbuf; sbuf.sem_num = 0; sbuf.sem_op = 1; sbuf.sem_flg = SEM_UNDO; ret = semop(semid, &sbuf, 1); if(-1 == ret) { perror("semop"); return; } } int main() { int shmid, semid, ret; void *shmaddr; int count = 0; shmid = shmget(SHMKEY, SHMSIZE, 0); if(-1 == shmid) { perror("shmget"); exit(1); } semid = semget(SEMKEY, 1, 0); if(semid == -1) { perror("semget"); exit(1); } shmaddr = shmat(shmid, NULL, 0); if(NULL == shmaddr) { perror("shmat"); exit(1); } while(1) { sem_p(semid); count = *(int *)shmaddr; //读取数据 usleep(100000); if(count >= 100) { break; } printf("Process B : count = %d\n", count); count++; *(int *)shmaddr = count; //写回共享内存 sem_v(semid); } shmdt(shmaddr); return 0; }
/************************************************************** > File Name: recv.c > Author: wow66lfy > Created Time: 2018年08月17日 星期五 11时53分27秒 **************************************************************/ #include <stdio.h> #include <stdlib.h> #include <sys/types.h> #include <sys/shm.h> #include <sys/ipc.h> #include <sys/sem.h> #define SHMKEY 1234 #define SHMSIZE 4096 #define SEMKEY 1234 union semun { int val; struct semid_ds *buf; unsigned short *array; struct seminfo *_buf; }; void sem_p(int semid) { int ret; struct sembuf sbuf; sbuf.sem_num = 0; sbuf.sem_op = -1; sbuf.sem_flg = SEM_UNDO; ret = semop(semid, &sbuf, 1); if(-1 == ret) { perror("semop"); return; } } void sem_v(int semid) { int ret; struct sembuf sbuf; sbuf.sem_num = 0; sbuf.sem_op = 1; sbuf.sem_flg = SEM_UNDO; ret = semop(semid, &sbuf, 1); if(-1 == ret) { perror("semop"); return; } } int main() { int shmid, semid, ret; void *shmaddr; int count = 0; shmid = shmget(SHMKEY, SHMSIZE, 0); if(-1 == shmid) { perror("shmget"); exit(1); } semid = semget(SEMKEY, 1, 0); if(semid == -1) { perror("semget"); exit(1); } shmaddr = shmat(shmid, NULL, 0); if(NULL == shmaddr) { perror("shmat"); exit(1); } while(1) { sem_p(semid); count = *(int *)shmaddr; //读取数据 usleep(100000); if(count >= 100) { break; } printf("Process B : count = %d\n", count); count++; *(int *)shmaddr = count; //写回共享内存 sem_v(semid); } shmdt(shmaddr); return 0; }
单打开A:
Process A : count = 0 Process A : count = 1 Process A : count = 2 Process A : count = 3 Process A : count = 4 ...(中间连续,以.代替了,否则太长了) Process A : count = 95 Process A : count = 96 Process A : count = 97 Process A : count = 98 Process A : count = 99
打开A后打开B:
(两个终端打开,分别运行) Process B : count = 5 Process B : count = 7 Process B : count = 9 Process B : count = 11 Process B : count = 13 (同) Process B : count = 93 Process B : count = 95 Process B : count = 97 Process B : count = 99 Process A : count = 0 Process A : count = 1 Process A : count = 2 Process A : count = 3 Process A : count = 4 Process A : count = 6 ...(同) Process A : count = 94 Process A : count = 96 Process A : count = 98
消息队列
消息队列就是一个消息的链表.可以把消息看作一个记录,具有特定的格式.进程可以向中按照一定的规则添加新消息;另一些进程则可以从消息队列中读走消息
一个本地单人聊天源代码(手动滑稽!):
/************************************************************** > File Name: msgget_recv.c > Author: wow66lfy > Created Time: 2018年08月17日 星期五 11时53分27秒 **************************************************************/ #include <stdio.h> #include<sys/types.h> #include<sys/ipc.h> #include<sys/msg.h> #include<stdlib.h> #include<string.h> #include <unistd.h> #include <sys/stat.h> #include <fcntl.h> #define MSGKEY 1234 struct msgbuf { long mtype; char mtext[64]; }; int main() { int ret; pid_t pid; struct msgbuf mbuf; int msgid = msgget(MSGKEY, 0); if(-1 == msgid ) { perror("msgget"); exit(1); } pid = fork(); if(-1 == pid) { perror("fork"); exit(1); } else if(0 == pid) //子程序发送 { while(1) { memset(&mbuf, 0, sizeof(mbuf)); mbuf.mtype = 2; //消息类型 scanf("%s", mbuf.mtext); ret = msgsnd(msgid, &mbuf, sizeof(mbuf.mtext), 0); if(-1 == ret) { perror("msgsnd"); exit(1); } if(!strcmp(mbuf.mtext, "bye")) { mbuf.mtype = 1; msgsnd(msgid, &mbuf, sizeof(mbuf.mtext), 0); break; } } } else //父程序接收 { while(1) { memset(&mbuf, 0, sizeof(mbuf)); ret = msgrcv(msgid, &mbuf, sizeof(mbuf.mtext), 1, 0); if(-1 == ret) { perror("msgrcv"); exit(1); } if(!strcmp(mbuf.mtext, "bye")) { kill(pid, 2); break; } printf("\t\t\t\t%s\n", mbuf.mtext); } } return 0; }
/************************************************************** > File Name: msgget_send.c > Author: Lifengyu > Created Time: 2018年08月17日 星期五 11时53分27秒 **************************************************************/ #include <stdio.h> #include<sys/types.h> #include<sys/ipc.h> #include<sys/msg.h> #include<stdlib.h> #include<string.h> #include <unistd.h> #include <sys/stat.h> #include <fcntl.h> #define MSGKEY 1234 struct msgbuf { long mtype; char mtext[64]; }; int main() { int ret; pid_t pid; struct msgbuf mbuf; int msgid = msgget(MSGKEY, IPC_CREAT | IPC_EXCL); if(-1 == msgid ) { perror("msgget"); exit(1); } pid = fork(); if(-1 == pid) { perror("fork"); exit(1); } else if(0 == pid) //子程序发送 { while(1) { memset(&mbuf, 0, sizeof(mbuf)); mbuf.mtype = 1; scanf("%s", mbuf.mtext); ret = msgsnd(msgid, &mbuf, sizeof(mbuf.mtext), 0); if(-1 == ret) { perror("msgsnd"); exit(1); } if(!strcmp(mbuf.mtext, "bye")) { mbuf.mtype = 2; msgsnd(msgid, &mbuf, sizeof(mbuf.mtext), 0); break; } } } else //父程序接收 { while(1) { memset(&mbuf, 0, sizeof(mbuf)); ret = msgrcv(msgid, &mbuf, sizeof(mbuf.mtext), 2, 0); if(-1 == ret) { perror("msgrcv"); exit(1); } if(!strcmp(mbuf.mtext, "bye")) { kill(pid, 2); break; } printf("\t\t\t\t%s\n", mbuf.mtext); } } sleep(1); msgctl(msgid, IPC_RMID, NULL); return 0; }
运行结果