零基础入门进程间通信:task 1(匿名管道与vscode使用)

目录

引言

VSCODE使用

进程间通信正题

基础背景

进程间通信分类

匿名管道

理解匿名管道

代码实现

匿名管道的特性

管道的四种情况

应用场景


引言

在当今的计算机技术领域,操作系统作为计算机系统的核心组件,承担着资源管理、任务调度和进程管理等重要职责。Linux作为一种开源、高性能、稳定的操作系统,广泛应用于服务器、嵌入式设备和个人电脑中。在Linux系统中,进程是资源分配和调度的基本单位,而进程间通信(Inter-Process Communication,IPC)则是确保多个进程能够协同工作、共享数据的关键技术。

本系列文章将通过task1-4来完成进程间通信的学习。

task 1将着重介绍:1.VScode远端连接linux云服务器 2.匿名管道通信

VSCODE使用

将VScode与linux远端的机器进行连接。

首先我们需要下载remote插件,F1找到remote添加远端主机指令。

输入用户名和远端主机的信息:ssh XXX@12345678

我们可以选择记住这个主机。

完成之后自己的用户内部有一个文件。

这个文件内部就添加了主机信息了(可以进行删除)

这样就提示连接上了远端。

需要注意的是,我们写完代码必须Ctrl + S保存才能保存文件。

从此我们就不需要用vim了,用vscode就可以进行代码编写

进程间通信正题

基础背景

进程通信的本质是:让不同的进程看到同一份资源。

这个资源一般是特定的内存空间,起这个资源一般是OS提供。

system V一般本机通信

posix一般网络通信

system V的通信标准给出了三个:消息队列、共享内存、信号量。

当然还有基于文件的通信:管道

进程间通信分类

管道
匿名管道 pipe
命名管道
System V IPC
System V 消息队列
System V 共享内存
System V 信号量
POSIX IPC
消息队列
共享内存
信号量
互斥量
条件变量
读写锁

匿名管道

什么是管道
管道是 Unix 中最古老的进程间通信的形式。
我们把从一个进程连接到另一个进程的一个数据流称为一个 " 管道 "。

理解匿名管道

父进程用wr的形式分别open打开一个文件

fork出子进程之后,子进程会继承父进程文件描述符表files_struct。指向同样的file结构

父子关闭不需要的接口,就可以实现资源的互联。

进程就像是用户,关闭打开某个文件,对文件产生的影响都是通过OS进行的。file结构内部存在计数器,关闭文件会造成计数器--。

匿名管道只能用于亲戚进程通信:父子可以、兄弟可以、孙子爷爷可以(只要有一样的files_struct就可以)。
fork 来共享管道原理


站在文件描述符角度 - 深度理解管道

站在内核角度 - 管道本质

open打开的是磁盘的文件,内存的文件怎么办?

pipe用于打开在内存级别的一个文件,这个文件也存在缓冲区......

创建一个管道pipe(用读和写的形式打开一个文件两次)。

大部分系统调用,用于判断是否成功,成功一般返回0,否则-1.

传参提醒你,传入一个两个元素组成的int数组,这是一个输出型参数

pipefd[0]是读文件打开方式的fd

pipe[1]是写方式打开文件的fd

代码实现

实现一个子进程写,父进程读的逻辑代码

cpp 复制代码
int main() 
{
    int pipefd[2] = {0};
    int ret = pipe(pipefd);     //创建内存管道文件,0是read端,1是write端。虽然fd不同,但是是同一个文件。
    if (ret < 0) 
    {
        cout << "Failed to create pipe" << endl;
        return -1;
    }

    // cout << "pipefd[0] = " << pipefd[0] << ", pipefd[1] = " << pipefd[1] << endl;

    pid_t id = fork();
    
    if (id < 0) 
    {
        cout << "Failed to fork" << endl;
        return -1;
    }

    //子进程写,父进程读
    if (id == 0)
    {
        // child process
        close(pipefd[0]);
        //ipc code 
        Writer(pipefd[1]);      //不断写

        close(pipefd[1]);
        exit(0);
    }

    // parent process
    close(pipefd[1]);
    //ipc code 

     //虽然子进程将pipepid[0]关闭了,但是只会让file结构--,父进程还是可以对pipefd[0]进行读操作的。
    Reader(pipefd[0]);      
    pid_t retid = waitpid(id, nullptr, 0);
    if (retid < 0)  
    {
        cout << "Failed to wait child process" << endl; 
        return 3;
    }
    close(pipefd[0]);

    return 0;
}    

写入缓冲区

cpp 复制代码
//child process
void Writer(int Wfd) 
{   
    string str = "Hello, im child process!";
    pid_t pid = getpid();
    int number = 0;

    char buffer[1024] = {0};

    while (true)
    {
        //构建发送字符串
        buffer[0] = '\0';   //清空缓冲区,告诉读者,这是一个字符串
        snprintf(buffer, sizeof(buffer), "%s pid = %d, number = %d\n", str.c_str(), pid, number++);
        // cout << buffer << endl;
        sleep(10);

        //发送字符串给父进程(只要你是一个文件,存在fd,就可以用write向文件写入内容)
        write(Wfd, buffer, strlen(buffer));     //write是写入到了文件缓冲区

    }
    

}

snprintf可以将数据格式化的写道字符串中

从缓冲区读取

cpp 复制代码
//parent process
void Reader(int Rfd) 
{
    char buffer[1024] = {0};
    while (true)
    {
        buffer[0] = '\0';   //清空缓冲区,告诉读者,这是一个字符串   

        //从文件中读取字符串
        ssize_t n = read(Rfd, buffer, sizeof(buffer));   //sizeof(buffer) != strlen(buffer)
        if (n > 0)   //n是读取的字节数
        {
            buffer[n] = 0;   //字符串末尾加上'\0'
            cout << "Received message: " << buffer << endl;
        }
    }
    
}

总结

核心逻辑就是父进程用pipe打开一个内存级别的文件两次

父进程创建子进程

子进程调用W函数,父进程调用R函数

W函数就是借助pipefd[1]向文件缓冲区写入(write)

R函数就是借助pipefd[0]从缓冲区读取(read)

父进程等待子进程

易错解析

每次打开一个文件都会产生一个file结构,同时获得一个file结构对应的fd。

读方式打开,获得读方式的fd

写方式打开,获得写方式的fd

一个进程可以用不同的方式打开一个文件多次。

我们不可以通过建立全局字符串的方式去进行通信,因为子进程在修改字符串时,会发生写时拷贝。

所有进行进程通信的时候,所占用的区域属于OS管理,而不是某个进程。

匿名管道的特性

1.只有亲戚之间可以通信

2.管道只能单向通信

3.父子进程协同、互斥(限定资源的抢占特性)、同步通信的特性

eg:子进程休眠10s才写入一次,那父进程也别着急读,会等待一下进程(并不会读取空的管道,因为没有打印Received message:,说明直接没有读)

4.管道面向字节流

不管你写的是什么,在r端认为都是一个个字节,只负责读。所谓的格式区分,不是r端该干的活,这种特性就是字节流。

5.管道基于文件,而文件被打开的生命周期基于进程,所以进程结束,管道关闭。

6.管道是有固定大小的,在不同的内核中,可能有差别

7.管道的原子性

**原子性:小于pipe_buf,就是原子的。**保证读写的连贯性 4kb

管道的四种情况

读写端正常,管道为空,读端阻塞

读写端正常,管道满了,写段阻塞

写端关闭,read会读取到EOF,返回0,不会阻塞

读端关闭,写端继续写入时,OS就要(通过信号)杀掉写进程

写端关闭特指的是:一定要现有写端被打开的现象,才能说是写端被关闭。如果写端都哦没有被打开,就不存在关闭一说

在命名管道(FIFO)的情况下,"写端关闭"这个说法确实指的是写端曾经被打开,并且随后被关闭。以下是详细解释:

打开写端:这意味着至少有一个进程通过 open 系统调用以写模式(O_WRONLY)打开了管道的写端,从而能够向管道写入数据。

关闭写端:这发生在进程完成写入操作后,通过 close 系统调用关闭了写端。关闭写端意味着该进程不再向管道写入数据,但如果有其他进程已经打开了写端,它们仍然可以写入。

写端未被打开:如果没有任何进程以写模式打开管道的写端,那么我们就不能说写端被关闭,因为关闭是一个状态改变,需要先有一个打开的状态。

因此,如果没有进程曾经打开过写端,那么说"写端被关闭"是不准确的。在这种情况下,更准确的说法是"写端尚未被打开"或"没有进程打开写端"。

当读端进程尝试从管道读取数据时,如果写端尚未被任何进程打开,那么读操作会阻塞,等待写端的打开和数据的写入。只有当至少一个进程打开了写端并写入数据后,读端进程的读操作才会解除阻塞并读取数据。如果所有写端都被关闭,并且没有数据留在管道中,那么读操作会返回0,表示到达了文件末尾。

应用场景

在bash中输入的|管道符号就是一种匿名管道。

相关推荐
淮北4943 小时前
vim学习进阶
学习·编辑器·vim
maosheng11463 小时前
RHCSA的第一次作业
linux·运维·服务器
wifi chicken4 小时前
Linux 端口扫描及拓展
linux·端口扫描·网络攻击
旺仔.2914 小时前
Linux 信号详解
linux·运维·网络
放飞梦想C4 小时前
CPU Cache
linux·cache
Hoshino.415 小时前
基于Linux中的数据库操作——下载与安装(1)
linux·运维·数据库
恒创科技HK5 小时前
通用型云服务器与计算型云服务器:您真正需要哪些配置?
运维·服务器
weixin_426689206 小时前
vscode C语言编译环境搭建(单个文件)
c语言·ide·vscode
播播资源6 小时前
CentOS系统 + 宝塔面板 部署 OpenClaw源码开发版完整教程
linux·运维·centos
源远流长jerry7 小时前
在 Ubuntu 22.04 上配置 Soft-RoCE 并运行 RDMA 测试程序
linux·服务器·网络·tcp/ip·ubuntu·架构·ip