进程间的通信:
1. 管道
2. 信号
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
3. SIGCHLD:如果一个进程收到SIGCHLD信号,表示有子进程结束了(可以配套wait(wstatus)来使用,等待进程结束)
5. signal:
typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);
功能:改变信号的处理方式
参数:
signum:信号的编号
handler:信号的处理方式
SIG_IGN 忽略处理
SIG_DFL 缺省处理
函数首地址 捕捉处理
返回值:
成功返回之前处理函数的首地址
失败返回SIG_ERR
6. pause:
int pause(void);
功能:让进程睡眠,直到接收到信号(捕捉)才能继续向下执行
7. alarm:
unsigned int alarm(unsigned int seconds);
功能:定时seconds秒后给调用进程发送SIGALRM信号
参数:
seconds:定时的秒数
返回值:
成功返回之前设定剩余的秒数
练习:利用alarm和pause函数实现sleep(5)的功能,写一个Mysleep函数
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
//为了不让alarm过完seconds后直接结束,在创建一个handler
void handler(int signo)
{
return;
}
int MySleep(int seconds)
{
signal(SIGALRM, handler);
alarm(seconds);
pause();
//最后再让SIGALRM信号回归原始
signal(SIGALRM, SIG_DFL);
return 0;
}
int main(void)
{
printf("start running\n");
MySleep(5);
printf("ending\n");
return 0;
}
8. kill:
int kill(pid_t pid, int sig);
功能:给PID对应的进程发送sig信号
参数:
pid:进程ID号
sig:信号的编号
返回值:
成功返回0
失败返回-1
练习:利用kill实现按下ctrl+\后父进程给子进程发消息(好大儿,爹回来了),子进程接收到后收到回复(您可算回来了,我快饿死了);按下ctrl+c后子进程再父进程发消息(爸,我回来了),父进程收到回复(快去写作业)。
#include "head.h"
pid_t pid;
void handler_childer(int signo)
{
if(signo == SIGINT)
{
printf("爸,我回来了!\n");
fflush(stdout);
kill(getppid(), SIGUSR1);
}
else if(signo == SIGUSR2)
{
printf("您可算回来了,我快饿死了!\n");
}
return;
}
void handler_father(int signo)
{
if(signo == SIGUSR1)
{
printf("快去写作业!\n");
}
else if(signo == SIGQUIT)
{
printf("好大儿,爹回来了!\n");
kill(pid, SIGUSR2);
}
return;
}
int main(void)
{
pid = fork();
if(pid == -1)
{
perror("fail to fork");
return -1;
}
if(pid == 0)
{
signal(SIGQUIT, SIG_IGN);
signal(SIGINT, handler_childer);
signal(SIGUSR2, handler_childer);
}
else if(pid > 0)
{
signal(SIGINT, SIG_IGN);
signal(SIGUSR1, handler_father);
signal(SIGQUIT, handler_father);
}
while(1)
{
}
return 0;
}
作业:
1. 编写一个进程循环执行while
当按下 SIGINT(ctrl + c) 打印SIGINT信号来了
当按下 SIGQUIT(ctrl + \) 打印SIGQUIT信号来了
当按下 SIGTSTP(ctrl + z) 打印SIGTSTP信号来了
#include "head.h"
void handlequit(int ret)
{
printf("SIGQUIT信号来了\n");
}
void handleint(int ret)
{
printf("SIGINT信号来了\n");
}
void handletstp(int ret)
{
printf("SIGSTOP信号来了\n");
}
int main(void)
{
void (*pret)(int) = NULL;
signal(SIGQUIT, handlequit);
signal(SIGINT, handleint);
signal(SIGTSTP, handletstp);
while(1)
{
}
return 0;
}
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;
}