当前位置: 首页 > news >正文

自学Linux系统软件编程七天

进程间的通信

  1. 进程间的通信:管道:进程间通信最简单的形式  ;信号:提供一种内核层与用户层之间的通知机制 ;消息队列 ; 共享内存 ;信号灯  ;套接字
  2. 管道:
    1. 无名管道
    只能用于具有亲缘关系的进程间通信
    2. 有名管道
    可以任意两个进程之间通信
    3. 有名管道必须读写两端同时加入,才能继续向下执行,否则会阻塞等待
     1. 管道特点:
    读:
    如果管道中有写端,有数据则直接读出,没有数据会阻塞等待直到有数据写入,读出数据才能执行向下执行 ;如果管道中没有写端,有数据则直接读出,没有数据不会阻塞等待,直接返回
    写:
    如果管道中有读端,管道中数据没有写满数据,则直接写入,如果管道中写满数据,需要等到有数据读出才能继续写入;如果管道中没有读端,向管道中写入数据会产生管道破裂             2. 函数接口:
    man 2 pipe
    int pipe(int pipefd[2]);
    功能:
    创建操作管道的两个文件描述符
    参数:
    pipefd:
    存放文件描述符的数组
    pipefd[0]:读文件描述符
    pipefd[1]:写文件描述符
    返回值:
    成功返回0
    失败返回-1
    man 3 mkfifo
    int mkfifo(const char *pathname, mode_t mode);
    功能:
    创建管道文件
    参数:
    pathname:管道文件名
    mode:权限
    返回值:
    成功返回0
    失败返回-1

  3. 信号
    1. 类型
    kill -l
    1) SIGHUP 2) SIGINT 3) SIGQUIT 4) SIGILL 5) SIGTRAP
    6) SIGABRT 7) SIGBUS 8) SIGFPE 9) SIGKILL 10) SIGUSR1
    11) SIGSEGV 12) SIGUSR2 13) SIGPIPE 14) SIGALRM 15) SIGTERM
    16) SIGSTKFLT 17) SIGCHLD 18) SIGCONT 19) SIGSTOP 20) SIGTSTP
    21) SIGTTIN 22) SIGTTOU 23) SIGURG 24) SIGXCPU 25) SIGXFSZ
    26) SIGVTALRM 27) SIGPROF 28) SIGWINCH 29) SIGIO 30) SIGPWR
    31) SIGSYS 34) SIGRTMIN 35) SIGRTMIN+1 36) SIGRTMIN+2 37)
    SIGRTMIN+3
    38) SIGRTMIN+4 39) SIGRTMIN+5 40) SIGRTMIN+6 41) SIGRTMIN+7 42)
    SIGRTMIN+8
    43) SIGRTMIN+9 44) SIGRTMIN+10 45) SIGRTMIN+11 46) SIGRTMIN+12 47)
    SIGRTMIN+13
    48) SIGRTMIN+14 49) SIGRTMIN+15 50) SIGRTMAX-14 51) SIGRTMAX-13 52)
    SIGRTMAX-1
    53) SIGRTMAX-11 54) SIGRTMAX-10 55) SIGRTMAX-9 56) SIGRTMAX-8 57)
    SIGRTMAX-7
    58) SIGRTMAX-6 59) SIGRTMAX-5 60) SIGRTMAX-4 61) SIGRTMAX-3 62)
    SIGRTMAX-2
    63) SIGRTMAX-1 64) SIGRTMAX
    2:SIGINT 中断 ctrl + c
    3:SIGQUIT 退出 ctrl + \
    11:SIGSEGV 段错误
    13:SIGPIPE 管道破裂
    14:SIGALRM 定时
    17:SIGCHLD 有子进程结束了
    18:SIGCONT 让进程暂停
    19:SIGSTOP 让进程停止
    20:SIGTSTP 让进程挂起 ctrl + z
    29:SIGIO 异步IO信号

    2. 对信号的处理方式:
    1. 忽略:信号来了,不处理该信号
    2. 缺省:信号来了,按照默认的方式处理信号
    3. 捕捉:信号来了,按照指定的方式处理信号
    注意:
    9号、19号信号,不能被忽略和捕捉

  4. 函数接口

    man 2 signal
    typedef void (*sighandler_t)(int);
    sighandler_t signal(int signum, sighandler_t handler);
    功能:
    修改信号的处理方式
    参数:
    signum:信号的编号
    handler:信号处理的方式
    SIG_IGN 忽略
    SIG_DFL 缺省
    返回值:
    成功返回之前信号处理函数
    失败返回SIG_ERR
    man 2 alarm
    unsigned int alarm(unsigned int seconds);
    功能:
    定时seconds秒后给当前进程发送SIGALRM信号
    参数:
    seconds:秒数
    返回值:
    成功返回之前定时剩余的秒数
    如果之前没有定时返回0
    man 2 kill
    int kill(pid_t pid, int sig);
    功能:
    向pid对应的进程发送sig信号
    参数:
    pid:目标进程的PID
    sig:信号的编号
    返回值:
    成功返回0
    失败返回-1
    

QUESTION 

练习:实现一收一发消息:我的思路是利用 mkfifo 函数创建有名管道,为进程间通信搭建通道。我创建l两个管道,一个用于发送消息,一个用于接收消息。然后使用 open 函数分别以只读、只写的模式打开管道。send.c 程序主要负责先发送消息,然后接收消息;recv.c 程序主要负责先接收消息,然后发送回复消息。

//send.c
#include <stdio.h>
#include "public.h"

int main(int argc, const char **argv)
{
	int fatob = 0;
	int fbtoa = 0;
	char tmpbuff[4096] = {0};

	mkfifo("/tmp/ATOB", 0777);
	mkfifo("/tmp/BTOA", 0777);

	fatob = open("/tmp/ATOB", O_WRONLY);
	if (-1 == fatob)
	{
		ERR_MSG("fail to open");
		return -1;
	}

	fbtoa = open("/tmp/BTOA", O_RDONLY);
	if (-1 == fbtoa)
	{
		ERR_MSG("fail to open");
		return -1;
	}

	printf("管道打开成功!\n");

	while (1)
	{
		gets(tmpbuff);
		write(fatob, tmpbuff, strlen(tmpbuff));

		memset(tmpbuff, 0, sizeof(tmpbuff));
		read(fbtoa, tmpbuff, sizeof(tmpbuff));
		printf("RECV:%s\n", tmpbuff);
	}
	
	close(fatob);
	close(fbtoa);

	return 0;
}

//recv.c
#include <stdio.h>
#include "public.h"

int main(int argc, const char **argv)
{
	int fatob = 0;
	int fbtoa = 0;
	char tmpbuff[4096] = {0};

	mkfifo("/tmp/ATOB", 0777);
	mkfifo("/tmp/BTOA", 0777);

	fatob = open("/tmp/ATOB", O_RDONLY);
	if (-1 == fatob)
	{
		ERR_MSG("fail to open");
		return -1;
	}

	fbtoa = open("/tmp/BTOA", O_WRONLY);
	if (-1 == fbtoa)
	{
		ERR_MSG("fail to open");
		return -1;
	}

	printf("管道打开成功!\n");

	while (1)
	{
		memset(tmpbuff, 0, sizeof(tmpbuff));
		read(fatob, tmpbuff, sizeof(tmpbuff));
		printf("RECV:%s\n", tmpbuff);

		gets(tmpbuff);
		write(fbtoa, tmpbuff, strlen(tmpbuff));
	}
	
	close(fatob);
	close(fbtoa);

	return 0;
}

写完上述代码后,我发现,当我在其中一端运行过后,想使用ctrl+c退出,但是之后另一端出现了一直在打印RECV,为了解决这个问题,我对代码进行优化处理,利用线程让两个程序能同时运行,并加入了在两端都输入.quit即可退出程序运行的代码,

//send.c
#include <stdio.h>
#include "public.h"

int fatob = 0;
int fbtoa = 0;

void *sendfun(void *arg)
{
	char tmpbuff[4096] = {0};

	while (1)
	{
		gets(tmpbuff);
		if (0 == strcmp(tmpbuff, ".quit"))
		{
			break;
		}
		write(fatob, tmpbuff, strlen(tmpbuff));
	}
	close(fatob);

	return NULL;
}

void *recvfun(void *arg)
{
	char tmpbuff[4096] = {0};
	ssize_t nret = 0;

	while (1)
	{
		memset(tmpbuff, 0, sizeof(tmpbuff));
		nret = read(fbtoa, tmpbuff, sizeof(tmpbuff));
		if (0 == nret)
		{
			break;
		}
		printf("RECV:%s\n", tmpbuff);
	}

	return NULL;
}

int main(int argc, const char **argv)
{
	pthread_t tid_send;
	pthread_t tid_recv;
	char tmpbuff[4096] = {0};

	mkfifo("/tmp/ATOB", 0777);
	mkfifo("/tmp/BTOA", 0777);

	fatob = open("/tmp/ATOB", O_WRONLY);
	if (-1 == fatob)
	{
		ERR_MSG("fail to open");
		return -1;
	}

	fbtoa = open("/tmp/BTOA", O_RDONLY);
	if (-1 == fbtoa)
	{
		ERR_MSG("fail to open");
		return -1;
	}

	printf("管道打开成功!\n");

	pthread_create(&tid_send, NULL, sendfun, NULL);
	pthread_create(&tid_recv, NULL, recvfun, NULL);

	pthread_join(tid_send, NULL);
	pthread_join(tid_recv, NULL);
#if 0
	while (1)
	{
		gets(tmpbuff);
		write(fatob, tmpbuff, strlen(tmpbuff));

		memset(tmpbuff, 0, sizeof(tmpbuff));
		read(fbtoa, tmpbuff, sizeof(tmpbuff));
		printf("RECV:%s\n", tmpbuff);
	}
#endif
	close(fbtoa);

	return 0;
}

//recv.c
#include <stdio.h>
#include "public.h"

int fatob = 0;
int fbtoa = 0;

void *sendfun(void *arg)
{
	char tmpbuff[4096] = {0};

	while (1)
	{
		gets(tmpbuff);
		if (0 == strcmp(tmpbuff, ".quit"))
		{
			break;
		}
		write(fbtoa, tmpbuff, strlen(tmpbuff));
	}
	close(fbtoa);

	return NULL;
}

void *recvfun(void *arg)
{
	char tmpbuff[4096] = {0};
	ssize_t nret = 0;
	
	while (1)
	{
		memset(tmpbuff, 0, sizeof(tmpbuff));
		nret = read(fatob, tmpbuff, sizeof(tmpbuff));
		if (0 == nret)
		{
			break;
		}
		printf("RECV:%s\n", tmpbuff);
	}

	return NULL;
}

int main(int argc, const char **argv)
{
	pthread_t tid_send;
	pthread_t tid_recv;
	char tmpbuff[4096] = {0};

	mkfifo("/tmp/ATOB", 0777);
	mkfifo("/tmp/BTOA", 0777);

	fatob = open("/tmp/ATOB", O_RDONLY);
	if (-1 == fatob)
	{
		ERR_MSG("fail to open");
		return -1;
	}

	fbtoa = open("/tmp/BTOA", O_WRONLY);
	if (-1 == fbtoa)
	{
		ERR_MSG("fail to open");
		return -1;
	}

	printf("管道打开成功!\n");

	pthread_create(&tid_send, NULL, sendfun, NULL);
	pthread_create(&tid_recv, NULL, recvfun, NULL);

	pthread_join(tid_send, NULL);
	pthread_join(tid_recv, NULL);
#if 0
	while (1)
	{
		gets(tmpbuff);
		write(fatob, tmpbuff, strlen(tmpbuff));

		memset(tmpbuff, 0, sizeof(tmpbuff));
		read(fbtoa, tmpbuff, sizeof(tmpbuff));
		printf("RECV:%s\n", tmpbuff);
	}
#endif
	close(fatob);

	return 0;
}

相关文章:

  • 【优选算法】四数之和
  • Python - 代码片段分享 - Excel 数据实时写入方法
  • 力扣LeetCode:1656 设计有序流
  • Python生成器250224
  • 代码随想录Day46 | 647.回文子串,516.最长回文子序列
  • CI/CD的定义
  • Docker 部署 Jenkins持续集成(CI)工具
  • 20250224-代码笔记02-class CVRPTrainer
  • 谈谈 ES 6.8 到 7.10 的功能变迁(3)- 查询方法篇
  • 开源神器KRR:用数据驱动K8s资源优化
  • 【C】堆的应用1 -- 堆排序
  • ubuntu安装配置docker
  • 《数据库索引设计与优化》译本错误纠正(1)
  • C++类和对象(中)
  • Ocelot 请求聚合
  • 【JavaScript】什么是JavaScript?以及常见的概念
  • jupyterhub on k8s 配置用户名密码 简单版
  • C++ day4 练习
  • SQL: DDL,DML,DCL,DTL,TCL,
  • 2.24力扣每日一题--设计有序流
  • 美国务卿:俄方将在数天内提出俄乌停火大纲
  • 小满:一庭栀子香
  • 中方敦促美国停止将溯源问题政治化
  • 中国需加强自主创新和国际合作,提升产业链供应链韧性
  • 外交部:国际调解院着眼以调解定分止争,更好维护国际公平正义
  • 魔都眼|邮轮港国际帆船赛启动,120名中外选手展开角逐