嵌入式学习第二十三天!(进程间通信:管道、信号)

本文详细介绍了进程间通信的两种主要方式:使用C语言中的管道(无名管道和有名管道)以及信号(包括信号类型、处理方式和相关函数)。通过实例展示了如何在两个进程间发送和接收消息,以及如何利用信号进行交互。
摘要由CSDN通过智能技术生成

进程间的通信:

  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;
}

  • 22
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值