进程间的通信:
1. 管道
2. 信号
3. 消息队列
4. 共享内存
5. 信号灯
6. 套接字
1. 管道:
1. 无名管道
无名管道只能用于具有亲缘关系的进程间通信
原因:无名管道没有名字,所有找不到管道的具体位置,那么在创建子进程前,先创建无名管道,那么子进程就会继承父进程所创建的无名管道,那么就可以实现,父子进程间的通信。
1. pipe:
int pipe(int pipefd[2]);
功能:创建一个无名管道
参数:
pipefd[0]:读管道文件描述符
pipefd[1]:写管道文件描述符
返回值:
成功返回0
失败返回-1
2. 无名管道特性:
1. 管道中至少有一个写端:
读取数据时,如果管道中有数据直接读取;管道中没有数据,阻塞等待直到有数据写入读出,继续向后执行
2. 管道中没有写端:
读取数据时,如果管道中有数据直接读取;管道中没有数据,不阻塞等待直接向下执行
3. 管道中至少有一个读端:
写入数据时,如果管道中没有存满(64K),则直接写入;管道中如果存满,则阻塞等待,直到有数据读出,才能继续写入
4. 管道中没有读端:
写入数据时,会产生管道破裂错误,导致程序崩溃
2. 有名管道:
打开管道文件 -> 读写管道文件 -> 关闭管道文件
注意:有名管道必须读写两端同时加入才能继续向下执行
1. mkfifo:
int mkfifo(const char *pathname, mode_t mode);
功能:创建一个管道文件
参数:
pathname:管道文件路径
mode:权限
返回值:
成功返回0
失败返回-1
练习:
编写两个进程,A、B ,A与B可以互相收发消息,并打印出对方的消息
clientA.c:
#include "head.h"int fatob = 0;
int fbtoa = 0;
pthread_t tid_atob;
pthread_t tid_btoa;void *thread_AtoB(void *arg)
{while(1){char tmpbuff[4096] = {0};memset(tmpbuff, 0, sizeof(tmpbuff));gets(tmpbuff);write(fatob, tmpbuff, strlen(tmpbuff));if(!strcmp(tmpbuff, ".quit")){break;}}pthread_cancel(tid_btoa);return NULL;
}void *thread_BtoA(void *arg)
{while(1){char tmpbuff[4096] = {0};memset(tmpbuff, 0, sizeof(tmpbuff));read(fbtoa, tmpbuff, sizeof(tmpbuff));printf("RECV:%s\n", tmpbuff);if(!strcmp(tmpbuff, ".quit")){break;}}pthread_cancel(tid_atob);return NULL;
}int main(void)
{mkfifo("/tmp/AtoB", 0777);mkfifo("/tmp/BtoA", 0777);fatob = open("/tmp/AtoB", O_WRONLY);if(fatob == -1){perror("fail to open");return -1;}fbtoa = open("/tmp/BtoA", O_RDONLY);if(fbtoa == -1){perror("fail to open");return -1;}pthread_create(&tid_atob, NULL, thread_AtoB, NULL);pthread_create(&tid_btoa, NULL, thread_BtoA, NULL);pthread_join(tid_atob, NULL);pthread_join(tid_btoa, NULL);close(fatob);close(fbtoa);return 0;
}
clientB.c:
#include "head.h"int fatob;
int fbtoa;
pthread_t tid_atob;
pthread_t tid_btoa;void *thread_AtoB(void *arg)
{char tmpbuff[4096] = {0};while(1){memset(tmpbuff, 0, sizeof(tmpbuff));read(fatob, tmpbuff, sizeof(tmpbuff));printf("RECV: %s\n", tmpbuff);if(!strcmp(tmpbuff, ".quit")){break;}}pthread_cancel(tid_btoa);return NULL;
}void *thread_BtoA(void *arg)
{char tmpbuff[4096] = {0};while(1){memset(tmpbuff, 0, sizeof(tmpbuff));gets(tmpbuff);write(fbtoa, tmpbuff, strlen(tmpbuff));if(!strcmp(tmpbuff, ".quit")){break;}}pthread_cancel(tid_atob);return NULL;}int main(void)
{mkfifo("/tmp/AtoB", 0777);mkfifo("/tmp/BtoA", 0777);fatob = open("/tmp/AtoB", O_RDONLY);if(fatob == -1){perror("fail to open");return -1;}fbtoa = open("/tmp/BtoA", O_WRONLY);if(fbtoa == -1){perror("fail to open");return -1;}pthread_create(&tid_atob, NULL, thread_AtoB, NULL);pthread_create(&tid_btoa, NULL, thread_BtoA, NULL);pthread_join(tid_atob, NULL);pthread_join(tid_btoa, NULL);close(fatob);close(fbtoa);return 0;}
2. 信号:
信号用来实现内核层和用户信息的交互,也可以用来实现进程间通信
1. 信号的种类:
利用kill -l查看所有信号的种类
2. 信号处理方式:
1. 缺省:
按照系统默认的方式处理
2. 忽略:
不响应信号
3. 捕捉:
按照自定义方式处理信号
4. 注意:
1. 9号信号SIGKILL和19号信号SIGSTOP,这两个信号不能被忽略和捕捉
2. 以下三个信号可以从键盘输入:
SIGINT:ctrl + c
SIGQUIT:ctrl + \
SIGTSTP:ctrl + z
5. signal:
typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);
功能:改变信号的处理方式
参数:
signum:信号的编号
handler:信号的处理方式
SIG_IGN 忽略处理
SIG_DFL 缺省处理
函数首地址 捕捉处理
返回值:
成功返回之前处理函数的首地址
失败返回SIG_ERR
作业:
1. 编写一个进程循环执行while
当按下 SIGINT(ctrl + c) 打印SIGINT信号来了
当按下 SIGQUIT(ctrl + \) 打印SIGQUIT信号来了
当按下 SIGTSTP(ctrl + z) 打印SIGTSTP信号来了
2. 1021 个位数统计 - PAT (Basic Level) Practice (中文) (pintia.cn)
#include <stdio.h>int PrintDigit(char *pnum)
{int digit[10] = {0};int i = 0;while(*pnum != '\0'){digit[*pnum - '0']++;pnum++;}for(i = 0; i < 10; i++){if(digit[i] != 0){printf("%d:%d\n", i, digit[i]);}}return 0;
}int main(void)
{char number[1000] = {0};gets(number);PrintDigit(number);return 0;}
3. 1014 福尔摩斯的约会 - PAT (Basic Level) Practice (中文) (pintia.cn)
#include <stdio.h>
#include <string.h>int GetMousse(char (*pmos)[64], int n)
{int i = 0;for(i = 0; i < n; i++){scanf("%s", pmos[i]);}return 0;
}int PrintfDecode(char (*pmos)[64], int n, char *pwd)
{int i = 0;char *ptmp1 = NULL;char *ptmp2 = NULL;char *day[7] = {"MON", "TUE", "WED", "THU", "FRI", "SAT", "SUN"};ptmp1 = pmos[0];ptmp2 = pmos[1];//找寻星期几while(*ptmp1 != '\0' && *ptmp2 != '\0'){if(*ptmp1 == *ptmp2 && *ptmp1 >= 'A' && *ptmp1 <= 'Z'){break;}ptmp1++;ptmp2++;}sprintf(pwd, "%s", day[*ptmp1 - 'A']);//找寻几点ptmp1++;ptmp2++;while(*ptmp1 != '\0' && *ptmp2 != '\0'){if(*ptmp1 == *ptmp2){break;}ptmp1++;ptmp2++;}if(*ptmp1 >= 'A' && *ptmp1 <= 'Z'){sprintf(pwd, "%s %d", pwd, *ptmp1 - 'A' + 10);}else{sprintf(pwd, "%s %d", pwd, *ptmp1 - '0');}ptmp1 = pmos[2];ptmp2 = pmos[3];//找寻分钟while(*ptmp1 != '\0' && *ptmp2 != '\0'){if(*ptmp1 == *ptmp2 && ((*ptmp1 >= 'A' && *ptmp1 <= 'Z') || (*ptmp1 >= 'a' && *ptmp1 <= 'z'))){break;}ptmp1++;ptmp2++;}sprintf(pwd, "%s:%02ld", pwd, ptmp1 - pmos[2]);return 0;
}int main(void)
{char mousse[4][64] = {0};char passwd[64] = {0};GetMousse(mousse, 4);PrintfDecode(mousse, 4, passwd);printf("%s\n", passwd);return 0;
}