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

Linux-----进程间通信

一、按通信范围分类

  1. 同一主机进程通信

    • 传统IPC方式:
      • 管道(无名管道、有名管道)
      • 信号(Signal)
    • System V IPC:
      • 共享内存(效率最高)
      • 消息队列
      • 信号量
    • POSIX IPC(较新标准):
      • POSIX消息队列
      • POSIX信号量
      • POSIX共享内存
  2. 跨主机进程通信

    • Socket网络通信

二、通信原理

  • 进程空间独立,必须通过内核中转
  • 内核在内核空间建立通信机制,用户空间通过系统调用访问

三、管道通信

1. 无名管道(Pipe)

特性:

  • 半双工通信(数据单向流动)
  • 仅用于亲缘关系进程(父子/兄弟进程)
  • 数据遵循FIFO原则
  • 最大容量64KB(可通过fcntl(fd, F_GETPIPE_SZ)查询)

解释:

    双工     ---发送和接收可以同时进行 --手机,电话
    半双工   ---发送端 和 接收端  同一个时刻之后有一个起效 ---对讲机 
    单工     ---发送端 接收端固定 --- 广播 

一、函数原型

#include <unistd.h>
int pipe(int pipefd[2]);

二、参数说明

  • pipefd[2]:输出参数,用于接收两个文件描述符的数组
    • pipefd[0]:管道的读端(从该描述符读取数据)
    • pipefd[1]:管道的写端(向该描述符写入数据)

三、返回值

  • 成功返回 0
  • 失败返回 -1,并设置 errno
    • EMFILE:进程打开的文件描述符过多
    • ENFILE:系统文件表已满
    • EFAULT:非法地址空间

四、基础用法示例

#include<stdio.h>
#include<unistd.h>
#include <sys/wait.h>
#include<string.h>
#include <stdlib.h>

int main(int argc, const char *argv[])
{
	int fd[2];
	int fd1[2];
	int ret = pipe(fd);
	ret = pipe(fd1);

	char buf[20] = {0};
	char buf1[1024] = {0};
	
	if(ret < 0)
	{
		perror("pipe fail");
		return -1;
	}

	pid_t pid = fork();

	if(pid < 0)
	{
		perror("fork fail");
		return -1;
	}
	
	if(pid > 0)
	{
		close(fd[0]);
		close(fd1[1]);
		while(1)
		{
			printf("f> ");
			fgets(buf,sizeof(buf),stdin);
			write(fd[1],buf,strlen(buf)+1);//加1是要保证输入的是字符串
			if(strncmp(buf,"quit",4) == 0)
			{
				wait(NULL);
				exit(EXIT_SUCCESS);
			}
			read(fd1[0],buf1,sizeof(buf));
			printf("buf1 = %s\n",buf1);
		}
	}else if(pid == 0)
	{
		close(fd[1]);
		close(fd1[0]);
		while(1)
		{
			read(fd[0],buf,sizeof(buf));
			if(strncmp(buf,"quit",4) == 0)
			{
				printf("child exit....\n");
				exit(EXIT_SUCCESS);
			}
			printf("buf = %s\n",buf);
			sprintf(buf1,"child %s",buf);
			write(fd1[1],buf1,strlen(buf1)+1);
		}
	}



	return 0;
}

五、关键特性说明

1. 数据流向
  • 单向流动:数据从写端(pipefd[1])流向读端(pipefd[0]
  • 半双工:同一时刻只能有一个方向的数据流
2. 原子性保证
  • 当写入数据量 ≤ PIPE_BUF(POSIX 要求 ≥ 512字节)时,保证写入操作的原子性
  • 可通过命令查看具体值:
    cat /proc/sys/fs/pipe-max-size  # 最大容量(默认 1MB)
    ulimit -a                       # 查看 PIPE_BUF 值(通常 4096)
    
3. 阻塞行为
场景读端行为写端行为
管道空 & 写端开放阻塞等待数据-
管道满 & 读端开放-阻塞直到有空间
所有读端关闭-触发 SIGPIPE 信号(默认终止进程)
所有写端关闭read 返回 0(EOF)-

四、mkfifo

一、函数原型

#include <sys/types.h>
#include <sys/stat.h>
int mkfifo(const char *pathname, mode_t mode);

二、参数说明

  • pathname:管道文件的路径(建议使用绝对路径)
  • mode:文件权限(实际权限受 umask 影响,建议搭配 umask(0) 使用)

三、返回值

  • 成功返回 0
  • 失败返回 -1,并设置 errno
    • EEXIST:文件已存在
    • ENOENT:路径不存在
    • EACCES:权限不足
    • ENOSPC:磁盘空间不足


四、基础用法示例

写入端代码
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <sys/wait.h>
#include <stdlib.h>
//./a.out fifo_A2B fifo_B2A
int main(int argc, const char *argv[])
{
	if (argc != 3)
	{
		printf("Usage: %s <fifo_A2B> <fifo_B2A>\n",argv[0]);
		return -1;
	}

	if(mkfifo(argv[1],0666) < 0 && errno != EEXIST)
	{
		perror("mkfifo fail");
		return -1;
	}
	if(mkfifo(argv[2],0666) < 0 && errno != EEXIST)
	{
		perror("mkfifo fail");
		return -1;
	}

	int fd1 = open(argv[1],O_WRONLY);
	int fd2 = open(argv[2],O_RDONLY);

	if (fd1 < 0 || fd2 < 0)
	{
		perror("open fail");
		return -1;
	}

	pid_t pid = fork();

	if (pid < 0)
	{
		perror("fork fail");
		return -1;
	}
	char buf[1024];
	if (pid > 0)
	{
		close(fd2);
		while (1)
		{
			printf("> ");
			fgets(buf,sizeof(buf),stdin);
			buf[strlen(buf)-1] = '\0';
			write(fd1,buf,strlen(buf)+1);
			
			if (strncmp(buf,"quit",4) == 0)
			{
				wait(NULL);
				printf("father exit......\n");
				exit(0);
			}
		}
	}else if (pid == 0)
	{
		close(fd1);
		while (1)
		{
			printf("c> ");
			read(fd2,buf,sizeof(buf));
			printf("%s \n",buf);
			if (strncmp(buf,"quit",4) == 0)
			{
				printf("child exit......\n");
				exit(0);
			}
		}

	}

	return 0;
}
读取端代码
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <sys/wait.h>
#include <stdlib.h>

//./a.out fifo_A2B fifo_B2A
int main(int argc, const char *argv[])
{
	if (argc != 3)
	{
		printf("Usage: %s <fifo_A2B> <fifo_B2A>\n",argv[0]);
		return -1;
	}

	if(mkfifo(argv[1],0666) < 0 && errno != EEXIST)
	{
		perror("mkfifo fail");
		return -1;
	}
	if(mkfifo(argv[2],0666) < 0 && errno != EEXIST)
	{
		perror("mkfifo fail");
		return -1;
	}

	int fd1 = open(argv[1],O_RDONLY);
	int fd2 = open(argv[2],O_WRONLY);

	if (fd1 < 0 || fd2 < 0)
	{
		perror("open fail");
		return -1;
	}

	pid_t pid = fork();

	if (pid < 0)
	{
		perror("fork fail");
		return -1;
	}
	char buf[1024];
	if (pid > 0)
	{
		close(fd1);
		while (1)
		{
			printf("> ");
			fgets(buf,sizeof(buf),stdin);
			buf[strlen(buf)-1] = '\0';
			write(fd2,buf,strlen(buf)+1);
			if (strncmp(buf,"quit",4) == 0)
			{
				wait(NULL);
				printf("father exit......\n");
				exit(0);
			}
		}
	}else if (pid == 0)
	{
		close(fd2);
		while (1)
		{
			printf("c> ");
			read(fd1,buf,sizeof(buf));
			printf("%s \n",buf);
			if (strncmp(buf,"quit",4) == 0)
			{
				printf("child exit......\n");
				exit(0);
			}
		}

	}

	return 0;
}

一、打开模式与阻塞关系表

打开方式读端行为写端行为是否推荐使用
O_RDONLY阻塞直到有写端打开-✅ 推荐
O_WRONLY-阻塞直到有读端打开✅ 推荐
O_RDWR立即返回(破坏FIFO语义)立即返回(破坏FIFO语义)❌ 禁止使用
`O_RDONLYO_NONBLOCK`立即返回(无数据返回0,需检查errno)-
`O_WRONLYO_NONBLOCK`-立即返回(无读端时返回ENXIO错误)

五、卸载管道unlink

一、函数原型

#include <unistd.h>
int unlink(const char *pathname);  // 正确拼写为 pathname

二、功能说明

  1. 核心作用

    • 删除文件系统中的一个目录项
    • 当文件引用计数归零时释放磁盘空间
  2. 对FIFO的特殊行为

    • 立即删除文件系统入口(文件不再可见)
    • 已打开的管道描述符仍可继续使用
    • 实际文件资源在所有进程关闭描述符后释放

三、参数说明

参数说明
pathname要删除的有名管道完整路径
示例:"/tmp/my_fifo"


四、返回值

返回值说明
0删除成功
-1删除失败,可通过 errno 获取错误原因

五、错误处理

errno 值触发场景处理方法
EACCES权限不足(文件/目录不可写)检查文件权限或使用 sudo
ENOENT文件不存在先检查文件是否存在
EISDIR路径是目录改用 rmdir 删除目录
EBUSY文件正在被使用(某些系统)关闭所有进程的文件描述符

六、使用示例

1. 基础用法
const char *fifo_path = "/tmp/my_fifo";

// 创建并删除管道
if (mkfifo(fifo_path, 0666) == -1) {
    perror("mkfifo error");
    exit(EXIT_FAILURE);
}

// 使用管道...

// 删除管道文件
if (unlink(fifo_path) == -1) {
    perror("unlink error");
    exit(EXIT_FAILURE);
}
2. 安全删除模式
#include <stdlib.h>
#include <signal.h>

// 注册退出清理函数
void cleanup() {
    if (unlink("/tmp/my_fifo") == -1 && errno != ENOENT) {
        perror("cleanup error");
    }
}

int main() {
    atexit(cleanup);  // 正常退出时调用
    signal(SIGTERM, cleanup);  // 捕获终止信号
    signal(SIGINT, cleanup);   // 捕获Ctrl+C
    
    // ...其他代码...
}


七、重要注意事项

  1. 延迟释放机制

    int fd = open("/tmp/fifo", O_RDONLY);
    unlink("/tmp/fifo");  // 立即删除文件系统入口
    read(fd, buf, size);  // 仍然可以正常读取数据
    close(fd);            // 此时真正释放资源
    
  2. 多进程场景

    • 建议由最后退出的进程执行删除
    • 可使用文件锁协调删除操作:
      flock(fd, LOCK_EX);
      unlink(path);
      flock(fd, LOCK_UN);
      
  3. 临时文件最佳实践

    // 创建临时管道(自动删除)
    char tmp_path[] = "/tmp/fifo_XXXXXX";
    mktemp(tmp_path);         // 生成唯一名称
    mkfifo(tmp_path, 0600);  // 严格权限
    unlink(tmp_path);         // 立即标记删除
    

八、与 remove() 的区别

特性unlink()remove()
标准来源POSIX 系统调用C标准库函数
目录处理不能删除目录可删除空目录
封装实现原始系统调用内部调用 unlink/rmdir
错误返回通过 errno 获取通过返回值判断
推荐场景删除文件/FIFO跨平台文件删除

九、开发建议

  1. 在程序启动时清理旧管道:

    if (access(fifo_path, F_OK) == 0) {
        unlink(fifo_path);
    }
    
  2. 使用绝对路径避免歧义:

    // 错误示例
    unlink("my_fifo");  // 可能误删其他目录文件
     
    // 正确示例
    unlink("/var/run/myapp_fifo");
    
  3. 监控文件状态:

    struct stat st;
    if (stat(fifo_path, &st) == 0) {
        if (S_ISFIFO(st.st_mode)) {
            // 确认是管道文件再删除
            unlink(fifo_path);
        }
    }
    

📌 关键点:unlink 只是删除文件链接,实际资源释放需要等待所有引用关闭。对于管道文件的清理,建议结合引用计数和进程生命周期管理来实现安全删除。

相关文章:

  • redis---字符串SDS(简单动态字符串)底层结构
  • MySQL80 配置主从复制方案(双主双从)
  • JavaScript 前端面试 5()
  • 最长递增子序列(贪心算法)思路+源码
  • 鸿蒙开发中 数组 find 的理解
  • 【代码随想录】第九章-动态规划(上)
  • 2.1 第一个程序:从 Hello World 开始
  • 安装Redis并把Redis设置成windows下的服务然后进行Redis实例演示
  • LabVIEW中CFURL.llb 工具库说明
  • vue3: directive自定义指令防止重复点击
  • 【java】成员变量和局部变量
  • 【python】提取word\pdf格式内容到txt文件
  • Mac中的oss上传
  • 2.3 变量
  • 【复习】Redis
  • 2.2 STM32F103C8T6最小系统板的四种有关固件的开发方式
  • Python Django系列—入门实例
  • 云原生降本之路:技术创新与应用解析
  • clickhouse--表引擎的使用
  • 防漏电保护,塔能物联运维为城市照明安全“上锁”
  • 网站建设用到的工具/免费建网站
  • 湖北工程建设信息网站/最新消息
  • 互联网网站建设公司/百度seo点击排名优化
  • 白城网站建设公司/免费行情网站
  • 手机助手app下载/一键优化清理
  • 怎么快速提高网站权重/seo网络优化招聘