Liunx:文件fd、重定向、管道

文件fd: 

        操作系统运行中一定存在着许多被打开的文件,这些文件需要被管理。一个进程会打开若干个文件。一个文件如果在操作系统中被打开,那么必须给该文件创建一个文件对象,包含被打开文件的各种属性。那么进程与文件的关系就变成了PCB和该文件对象的关系。

        未打开的文件被保存在磁盘中,所以fopen注定是一个要访问磁盘的过程,也就是访问IO的过程。

        fopen需要的参数 ,当前路径:指的是进程当前所在的路径。fopen一定是被某个进程所调用的,这个当前路径指的就是该进程的当前路径。进程的属性中是包含了该进程所在的路径的。

fe478a791c4a41399bde212f71a366be.png

        所以调用fopen时,系统会把 进程的当前路径带上,cwd--current work direction。

        如果修改进程的cwd,那么fopen就会把文件默认新建在这个修改后的目录上。chdir该系统调用可以为进程改变当前路径:

dcc64e3115c947c6855165165a790c17.png

        fwrite()在写入之前都会将文件进行清空。cat log > txt .重定向到一个文件每次都是清空的写。效果相当于这里的fwrite()一w的属性打卡文件。

        \0终止符是c的规定,而不是文件的规定。

        C/c++都会默认打开三个输入输出流:

2c68ed7a27974af480e7b38bbd8850a0.png

文件操作系统调用:

        访问文件本质是访问硬件,所有访问硬件的库函数一定要封装系统调用接口。

        2 号man手册。open();

588ae517e3db4c799a72b917c2a5e18b.png

        比特级别的标志位:

5464d61672d9455690594371dc6a456c.png

         open系统调用返回的整形是文件描述符,分配规则是从0下标开始没有被使用的位置。

        0是stdin ,1是stdout,2是stderr。关掉其中之一的输入输出流,然后在使用open打开一个文件,该文件描述符被分配为那个被关掉的io流的描述符,那么从现象上看,close(1),之后再打开文件,之后的cout,printf依旧会向描述符为1的文件中输出,就像是输出被重新定向。所以输出重定向说的就是将进程默认打开的输入输出修改为自己指定的文件。

752f7f6dcd67426ca05a5e895a7836b1.png

 重定向:

        当close关闭某个默认流以后,使用C库函数中的文件操作会出现与open同样的效果。

        即可在用户层或者操作系统层修改文件描述符表,但是用户级别的进程依旧向规定的输出写入。

        修改文件描述符表的系统接口:dup2()

381d76063d1f4471b267ff904262633a.png

        oldfd覆盖newfd

        open系统调用返回的int整形是一个表的下标,而其内容是一个指向文件结构体的指针,将相应文件描述符,即表中相应下标位置的指针修改,就能完成一次重定向,即不再需要手动close()再重新打开;

        这里dup接收的两个整形,注意c的文件接口是一个指针类型的,用c接口打开文件是不知道文件fd,所以dup2 ()通常与open()系统调用配合使用。open()返回的直接是新打开文件的文件描述符。

        当把open()的打开方式的参数设置为追加写时,输出重定向>的效果变成了追加重定向>>。

        read()系统调用的返回值返回一个整形,即读到了多少个字节。read()的参数中有一个指定读多少个字节参数。有时实际读到的大小不一定是期望的大小。这就是这个返回值的含义。

        若将标准输入修改成一个文件,即本来从键盘读,变成从一个文件中读,这就是cat指令的原理。输入重定向。

1f720edfa8e84042bc424847c0ba96c1.png

 进程的替换,包括数据和执行代码的替换与打开的文件之间存在这隔离,进程的替换不会影响当前进程的所打开的关联文件。相应的文件描述符指向哪些文件,进程替换后依旧指向原来的文件。

 为什么要有两个文件描述符指向标准输出

       c0d3ad120d0d48abb5b5e425d6089408.png

        现象是被重定向的只是stdout,保证程序在需要重定向后,依旧可以向显示屏打印错误。

        重定向指令的全写:文件名后加文件描述符

8f6c8fc1d3034d14bb6cbe903f1af757.png

进程间通信 :    

        vscode链接在进程间通信上一节。

        进程间通信:什么是通信,为什么通信,怎么通信?

        通信就是一个进程想把数据给另一个进程,但是因为进程之间具有独立性,所以直接给是不行的,所以必须有通信的方案。

        通信传递数据指令或者进行多进程之间的协同,或者一个进程想通知另一个进程某些事情发生了

        怎么通信?

        首先要让通信的双方看到同一份公共资源,这个资源不能属于通信的任何一方,一般而言,这个资源由操作系统提供,但是操作系统一般不会让进程访问自己的资源,所以要实现进程访问该资源,就要提供一系列的系统调用接口。所以在底层,由操作系统提供通信的方案和一系列接口。一系列的标准被定义。

3af61b6e75d04138b7428e184e5c3dc2.png

e6bad8a04bb047c4937d607122c6fa96.png

        前者使用的最多的,后者需要了解。

        首先,在没有这个标准时,进程间通信可以基于文件的方式进行通信,即管道。

        管道是unix最古老的的通信方式,把从一个进程连接到另一个进程的数据流叫做管道。

        首先一个文件如果可以被多个进程打开并访问,那么该文件不就直接作为公共资源了吗?一个进程写,一个进程读。思想是对的,但是基于文件级的通信,意味着要访问外设,那么随之而来的就是效率问题。

        管道基于上述思想,但是不真正的向磁盘做刷新,可以向内存刷新 。

5c80a41c235e4dc4aa393dadb2c8b9f9.png

        原理、接口与编程:

        a5bce62a87cc4556ac51ef0815ed894d.png        

         操作系统可以为进程打开一种内存级文件,这种文件不真正的在磁盘上有相应的存储,而只是在内存中,并向其他的普通文件一样,有着文件inode,以及一系列方法。

        我们创建一个进程,该进程fork出一个子进程,该子进程复制了父进程的task_struct,包括file_struct,这个file_struct保存着该进程可以访问的文件的详细信息。这张表是被复制的,子进程父进程各有一分,但是该表指向的资源确实同一份:

f0a131b0a6e044fcbff6a5d5bfd78838.png

         这就意味着父子进程可以看到同一份内存级文件。管道就是文件。

        一个问题在于,父进程若是以只读的方式打开文件,那么子进程也只有只读的权限,故两进程都是只读,如何通信??
 

9d940a515ae44957bb6712bfa1744518.png

        解决办法是,父进程以读写的方式打开文件,子进程又具有读写权限,再根据具体场景的需求,父子进程选择性的关闭读写端 。

        一个进程对打开一个文件两次,一次以只读打开,一次以写方式打开。用这种方式是因为,如果对一个文件以读写的方式打开,该进程仅创建一个file结构体,fork()出子进程,相应的复制该结构体,该结构体中仅包含一个文件的偏移量,当你写入以后,偏移量指向文件末尾,那么如何读??子进程每次都要调整文件偏移量再读吗?

        所以我们将一个文件打开两次,OS为该进程创建了两个file结构体,然后父子进程选择性的关闭读写端,那么文件的读写偏移量就互不影响。

        另一个问题是,父子进程各自关闭一个读端一个写端,意味着两个进程之间只能选择单向通信,为什么不将各自进程的读写端都保留,实现双向通信呢??原因在于,虽然文件打开了两次,并且有两个file结构体,但是两个结构体指向的同一个文件缓冲区,当父进程写入,他接下来要进行读,如何判断这次读取的缓冲区的内容是我上次写入的还是子进程写入的??

        如果硬要以这种文件级的形式实现双向通信,当然可以单独的设计出一套数据结构来实现,但是我们直接使用文件这一套现有的结构,目的就是为了简便,所以这里只实现了单向通信。

        所以这也是为什么这种通信方式叫做管道的原因,像生活中的自来水管道等等此类,都是一端入,一端出。

54684b806adf468db9ae567e7086c50f.png

         这种方式的的通信,只要需要通信的进程双方,有一方是直接或者间接被fork()出的,也就是二者之间存在着父子,兄弟或者祖孙关系,就可以实现通信。

        同时这种通信方式,我们不需要内存级文件的名称或者inode,所以叫做再加个定语叫匿名管道。

接口与编程:

        到此为止,上述一直在阐述原理,还没有实现一个真正的通信。下面介绍OS为我们封装的接口。

        man 2 pipe:

        

f3584eb69b6649aab62d474c89435caa.png

        他的作用就是为我们以读写的方式打开一个内存级文件,传入一个输出型参数,将它为我们分别以读和写方式创建的文件描述符返回。0下标为读端,1下标位置为写端。

        

#include<iostream>
#include<unistd.h>
#include<string.h>
#include <sys/types.h>
       #include <sys/wait.h>



using namespace std;

//pipe
//
int main()
{
    pid_t pid_1 =getpid();
    cout<<"main::pid::"<<pid_1<<endl;

    int pipefd[2]={0};
    int n=pipe(pipefd);
    if(n<0)
    {
        cout<<"pipe2() error::"<<endl;
    }
    else
    {
        cout<<"create sucessfully pipe... "<<endl;
    }

    cout<<"fork...."<<endl;
    pid_t ch_pid=fork(); //创建子进程
    if(ch_pid<0)
    {
        cout<<" fork sucessfully ...."<<endl<<endl;
        //sleep(1);
    }

    if(ch_pid==0)
    
    {
        char buffer[1024]="0"; //子进程缓冲区

        //children
        //wirte
        cout<<"child::close side of read..."<<endl;
        sleep(1);
        close(pipefd[0]); //关闭读端

        cout<<"child ::input buffer..."<<endl;

        snprintf(buffer,sizeof(buffer)-1," I am child : pid::%d",getpid());

        //cout<<"child::buffer::content::"<<buffer<<endl;

        sleep(1);
        cout<<"child::write....."<<endl;

        n =write(pipefd[1],buffer,strlen(buffer));
        sleep(1);
        if(n>0)
        {
        cout<<"child::write sucessfull ..."<<endl;
        }
        else 
        {
            cout<<"child::wrte::error..."<<endl;
        }

        //sleep(5);
        

        
    }

    //father
    
    close(pipefd[1]);//关闭写端

    sleep(5); //等待子进程写
    char buffer[1024]="0";
    
    cout<<"father::read...."<<endl;
    n =read(pipefd[0],buffer,sizeof(buffer));
    if(n>0)
    {
        buffer[n]=0;
        cout<<"father::read sucessfully.... "<<endl;;

    }
    cout<<"father::content:"<<buffer<<endl;
    

   n= waitpid(ch_pid,0,0);
   if(n>0)
   {
       cout<<"wait sucessful ..."<<endl;
   }
    
    return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/911557.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【AIGC】ChatGPT提示词Prompt高效编写技巧:逆向拆解OpenAI官方提示词

博客主页&#xff1a; [小ᶻZ࿆] 本文专栏: AIGC | ChatGPT 文章目录 &#x1f4af;前言&#x1f4af;OpenAI官方提示词的介绍OpenAI官方提示词的结构与组成如何通过分析提示词找到其核心组件 &#x1f4af;OpenAI官方提示词分析案例一&#xff1a;制定教学计划案例二&…

干部谈话考察系统:革新传统,精准高效

在干部选拔任用和考核评价的过程中&#xff0c;谈话考察一直是关键环节之一。然而&#xff0c;传统的谈话考察方式却面临着诸多痛点&#xff0c;严重影响了干部考察工作的质量和效率。干部谈话考察系统的出现&#xff0c;为解决这些问题提供了有力的武器。 一、传统谈话考察的…

细说STM32单片机USART中断收发RTC实时时间并改善其鲁棒性的另一种方法

目录 一、工程目的 1、目标 2、通讯协议及应对错误指令的处理目标 二、工程设置 三、程序改进 四、下载与调试 1、合规的指令 2、不以#开头&#xff0c;但以&#xff1b;结束&#xff0c;长度不限 3、以#开头&#xff0c;不以;结束&#xff0c;也不包含;&#xff0c;长…

轨迹规划中优化预测:学习多个初始解的优化器

Abstract 在许多应用中&#xff0c;如机器人控制、自动驾驶和投资组合管理&#xff0c;需要在严格的运行时间限制下连续地解决相似的优化问题。在这种情况下&#xff0c;局部优化方法的性能对初始解的质量非常敏感&#xff1a;不良的初始化可能会导致收敛缓慢或得到次优解。为…

Xserver v1.4.2发布,支持自动重载 nginx 配置

Xserver——优雅、强大的 php 集成开发环境 本次更新为大家带来了更好的用户体验。 &#x1f389; 下载依赖组件时&#xff0c;显示进度条&#xff0c;展示下载进度。 &#x1f389; 保存站点信息和手动修改 vhost 配置文件之后&#xff0c;自动重载 nginx 配置 &#x1f41e…

idea 基础简单应用(java)

Java IDE&#xff08;集成开发环境&#xff09;的使用方法因不同的IDE而异&#xff0c;但通常都包含一些基本的操作和功能。以下以IntelliJ IDEA这一流行的Java IDE为例&#xff0c;介绍Java IDE的基本使用方法与指南&#xff1a; 一、下载与安装 请点击观看 idea免费安装步…

Notepad++ 更改字体大小和颜色

前言 在长时间编程或文本编辑过程中&#xff0c;合适的字体大小和颜色可以显著提高工作效率和减少眼睛疲劳。Notepad 提供了丰富的自定义选项&#xff0c;让你可以根据个人喜好调整编辑器的外观。 步骤详解 1. 更改字体大小 打开 Notepad 启动 Notepad 编辑器。 进入设置菜…

五个高质量伤感视频素材资源站,帮你快速找到完美创作素材

在制作短视频、MV或者广告时&#xff0c;伤感主题的视频素材往往能触动观众的情感&#xff0c;让作品更具共鸣。无论是表达分手、离别&#xff0c;还是展现孤独与失落&#xff0c;合适的伤感素材对情感类创作至关重要。为帮助创作者找到优质的视频素材&#xff0c;以下推荐5个高…

理解Web登录机制:会话管理与跟踪技术解析(一)

在这篇博客中&#xff0c;我们将深入探讨登录校验、会话技术和会话跟踪技术的基本概念、实现原理及其在Web应用中的应用。我们将介绍常见的会话跟踪技术&#xff0c;如Cookies、Session&#xff0c;并讨论它们的优缺点。同时&#xff0c;我们也会涉及如何使用现有的技术栈来实现…

ffmpeg:视频字幕嵌入(GPU加速)

实现方案 参考指令 ffmpeg -i input_video.mp4 -vf "subtitlessubtitles.srt" output_video.mp4 解决因文件名称复杂导致的指令执行失败问题&#xff08;引号给文件框起来&#xff09; ffmpeg -i "A.mp4" -vf "subtitlesB.srt" "c.mp4&qu…

qt QListWidget详解

1、概述 QListWidget 是 Qt 框架中的一个类&#xff0c;它提供了一个基于模型的视图&#xff0c;用于显示项目的列表。QListWidget 继承自 QAbstractItemView 并为项目列表提供了一个直观的接口。与 QTreeView 和 QTableView 不同&#xff0c;QListWidget 是专门为单行或多行项…

UE5 材质篇 0 创建一个材质

首先在starter里的shape里拖入一个几何到场景里 我选了个sphere 然后开始制作一个材质&#xff0c;直接右键点击 进入材质的蓝图界面 先来个纹理采样 左侧detail里选个图给他 type这里可以指定他是其他图片&#xff0c;例如normal map 采样一个之前给UV加个动态offset

AOSP沙盒android 11

这里介绍一下aosp装系统 什么是aosp AOSP&#xff08;Android Open Source Project&#xff09;是Android操作系统的开源版本。 它由Google主导&#xff0c;提供了Android的源代码和相关工具&#xff0c;供开发者使用和修改。 AOSP包含了Android的核心组件和API&#xff0c;使…

Linux挖矿病毒(kswapd0进程使cpu爆满)

一、摘要 事情起因:有台测试服务器很久没用了&#xff0c;突然监控到CPU飙到了95以上&#xff0c;并且阿里云服务器厂商还发送了通知消息&#xff0c;【阿里云】尊敬的xxh: 经检测您的阿里云服务&#xff08;ECS实例&#xff09;i-xxx存在挖矿活动。因此很明确服务器中挖矿病毒…

flink 内存配置(二):设置TaskManager内存

flink 内存配置&#xff08;一&#xff09;&#xff1a;设置Flink进程内存 flink 内存配置&#xff08;二&#xff09;&#xff1a;设置TaskManager内存 flink 内存配置&#xff08;三&#xff09;&#xff1a;设置JobManager内存 flink 内存配置&#xff08;四&#xff09;…

基于YOLO11/v10/v8/v5深度学习的建筑墙面损伤检测系统设计与实现【python源码+Pyqt5界面+数据集+训练代码】

《------往期经典推荐------》 一、AI应用软件开发实战专栏【链接】 项目名称项目名称1.【人脸识别与管理系统开发】2.【车牌识别与自动收费管理系统开发】3.【手势识别系统开发】4.【人脸面部活体检测系统开发】5.【图片风格快速迁移软件开发】6.【人脸表表情识别系统】7.【…

sheng的学习笔记-tidb框架原理

目录 TiDB整体架构 TiDB架构图 组件-TiDB Server 架构图 流程 关系型数据转成kv ​编辑 组件-TiKV Server​ 架构图 主要功能&#xff1a; 列簇 组件-列存储TiFlash 组件-分布式协调层&#xff1a;PD PD架构图 路由 Region Cache back off TSO分配 概念 解…

HarmonyOS-消息推送

一. 服务简述 Push Kit&#xff08;推送服务&#xff09;是华为提供的消息推送平台&#xff0c;建立了从云端到终端的消息推送通道。所有HarmonyOS 应用可通过集成 Push Kit&#xff0c;实现向应用实时推送消息&#xff0c;使消息易见&#xff0c;构筑良好的用户关系&#xff0…

linux 安装anaconda3

1.下载 使用repo镜像网址下载对应安装包 右击获取下载地址&#xff0c;使用终端下载 wget https://repo.anaconda.com/archive/Anaconda3-2024.02-1-Linux-x86_64.sh2.安装 使用以下命令可直接指定位置 bash Anaconda3-2024.02-1-Linux-x86_64.sh -b -p /home/anaconda3也…

如何学习C++游戏开发

学习C游戏开发是一个涉及多个领域的复杂过程&#xff0c;包括编程、游戏设计、图形学等。 1. **学习C基础**&#xff1a; - 掌握C的基本语法和面向对象编程。 - 学习C标准库&#xff0c;特别是STL&#xff08;标准模板库&#xff09;。 2. **理解游戏开发概念**&#xf…