Linux网络编程————进程间通信

本文介绍了Linux系统中进程间通信的几种方式,包括管道(无名管道和命名管道)、信号量、共享内存以及消息队列。详细阐述了每种通信方式的原理、特点和使用场景,提供了示例代码来说明如何实现这些通信机制。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

引言

进程间实现通信的方式,借助内核开辟一段缓存、借助文件、借助IPC对象、通过TCP/IP建立两个进程间的连接进行通信

管道(pipe)

管道的实质是一个内核缓冲区,进程以先进先出(FIFO, First In First Out)的方式从缓冲区存取数据:管道一端的进程顺序地将进程数据写入缓冲区,另一端的进程则顺序地读取数据,该缓冲区可以看做一个循环队列,读和写的位置都是自动增加的,一个数据只能被读一次,读出以后再缓冲区都不复存在了

局限性

1)半双工,数据只能在一个方向流动    
2)管道只能在具有公共祖先之间的两个进程之间使用,大多是父子进程之间的通信,后面讲命名管道

父子进程间通过管道通信实例
#include <unistd.h>
int pipe(int fd[2]); //fd[0]为读,fd[1]为写,返回值:若成功,返回0,若出错,返回-1. 

通常,进程会先调用pipe,接着调用fork
在这里插入图片描述

fork之后做什么取决于我们想要的数据流的方向,对于从父进程到子进程,父进程关闭管道的读端fd[0],子进程关闭写端
fd[1],反之从子进程到父进程,子进程关闭管道fd[0],父进程关闭管道的读端fd[1]

在这里插入图片描述

#include <unistd.h>
#include <stdio.h>
#include <string.h>
//int pipe(int pipefd[2]);
int main()
{
   
 //思路:先pipe()再fork(),再根据数据流向关闭两端相应端口
 pid_t   	pid; 
 int     	pipe_fd[2];
 char    	buf[30];
 int     	rd;
 
 if(pipe(pipe_fd)<0)//先创建pipe,默认创建时就一端读一端写
 {
   
  	printf("pipe create failure!\n");
  	return -1;
 }
 
 pid=fork();
 if(pid<0)//出错
 {
   
  	printf("create child fork  failure!\n");
  	return -2;
 }
 
 if(pid==0)//子进程创建成功,根据数据的流向方向,开始对子进程的进出端口操作 
 {
   
  	close(pipe_fd[1]);//关写端口
  	rd=read(pipe_fd[0],buf,sizeof(buf));//从读端口读数据
    if(rd<0)
  	{
   
   		printf("read data failture!\n");
   		return -3;
  	}
  	printf("I am you child process ,received data is:%s\n",buf);
 }
 
 close(pipe_fd[0]);//根据数据的流向方向,操作父进程的进出端口
 write(pipe_fd[1],"hello",strlen("hello"));
 sleep(1);
 return 0;

如果要实现进程间的双向通信则需要使用两个无名管道,或者下面的命名管道

命名管道(fifo)

适用范围:任何两个进程都能交换数据
不足:是通过open来打开管道文件的,可能会造成阻塞
这里要记录一下的是就是open后的阻塞问题,如果设置成只读那么在数据没来的时候就是一直在这里堵着,只写也是一样,你没有开始写的时候他也一直等,其他的都还好。

思想:fifo先是创建文件用来存放数据,让后通过mknode系统调用来完成在磁盘上建立对应的节点,注意这里的节点只是在磁盘上拥有一个名字和相应的访问权限,并不是把数据存放在磁盘上,因为我们的Linux核心思想就是"一切接文件”,我们知道我们电脑运行的数据都放在存储介质上,所以只要我们想要通信的进程在存储介质上有相应的节点和访问权限,其他进程就可以找到节点并访问相应路径下的文件来拿到数据
代码的话这里可能有点放不下,就链接一下吧!
这里的代码主要是实现两个进程间通过一个命名管道来半双工通信,同一时间一个进程只能写,另一个只能读,如果要全双工就要多路复用了,这里就不多扯了,里面有脚本可以自己搞过去玩一下,我的代码大概功能就是这副图
在这里插入图片描述
cline
server

IPC对象

在说信号量、消息队列、共享内存之前先说一下IPC对象,IPC对象是活动在内核级别的一种进程间通信的工具,对于内核而言IPC对象提供一个唯一的标识符供内核管理,对于进程而言,IPC对象提供key值来关联进程,实现被进程调用,这个IPC对象可以是消息队列或信号量或共享存储器中的任意一种类型,对于不同的IPC对象,进程在调用IPC对象时的函数也有所差别

常见的IPC调用

  1. IPC对象的创建函数
  • 信号量:semget()
  • 消息队列:msgget()
  • 共享内存:shmget()

内核为每个IPC对象设置了一个ipc_perm的结构体里面保存着IPC对象的访问权限和所有者等信息,不同类型的IPC对象ipc_perm结构体中内容也有所差别,详情可以去查看Linux内核的<ipc.h>头文件,这里就略讲了
参考资料1
参考资料2

  1. IPC对象控制函数
  • 信号量:msgctl ()
  • 消息队列:semctl ()
  • 共享内存:shmctl ()
  1. IPC对象操作函数:
  • 信号量:semop()。信号量的P,V操作通过该函数实现
  • 消息队列:msgsnd()、msgrcv()。消息队列通过这一对函数来实现数据的收发
  • 共享内存:shmat()、shmdt()。共享内存通过这对函数实现进程与共享内存的关联与分离

信号量(semaphore)

首先进程不能通过信号量传递数据,信号量只是用来实现进程通信的同步与互斥,比如说在父子进程的情况,fork之后谁先运行都是操作系统说了算的,一般sleep一下让子进程先走,但是实际上子进程运行完需要的时间是不确定的,你sleep短了子进程还没搞完,sleep长了子进程已经再喝茶了,所以通过sleep是不精准的,而信号量则是,我一用完就让你来,很衔接很同步,我在用的时候你一定不能来捣乱必须我这个进程结束才轮到你,规定很明确可以很精准的控制公共资源的访问。

在操作系统中,信号量仅能表示当前与该信号量相关物理资源的数量。进程的互斥与同步对应着信号量的PV操作,这里拿信号量保护下的资源只能被一个进程访问来说,即互斥锁的那种状况下PV操作
P操作:当信号量的值小于0的时候,表示没有资源可以拿了,其他进程拿不到资源,进入等待队列,等待正在使用的进程把资源释放,即让信号量大于0

V操作:当进程用完资源后,即将释放资源,先检查有没有其他进程在等待,有的话唤醒等待队列中第一个进程,也就是最先进来等待的进程,如果没有就直接释放资源,信号量值加1
下面就拿上面说的fork来PV操作一下
参考资料

#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <unistd.h>

//思路:fork之后在子进程中用V
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值