Linux之重定向和缓冲区详细解析

news2024/12/25 0:04:25

个人主页:点我进入主页

专栏分类:C语言初阶  C语言进阶  数据结构初阶    Linux    C++初阶      算法   C++进阶

欢迎大家点赞,评论,收藏。

一起努力,一起奔赴大厂

目录

一.重定向

 1.1stat接口

 1.1.1利用stat查看文件大小

 1.1.2利用stat读取文件

1.2文件描述符分配规则

1.2.1分别关闭文件描述符

 1.2.2只关闭1

1.3dup2

1.4标准错误重定向

二.缓冲区

2.1刷新策略

2.2封装一下库


一.重定向

 1.1stat接口

        首先我们先写入文件

#include<stdio.h>
#include<string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
int main()
{
    int fd=open("test.txt",O_CREAT|O_WRONLY|O_TRUNC,0666);
    if(fd<0)
    {
        perror("fopen\n");
        return 1;
    }
    const char * message="hello Linux\n";
    write(fd,message,strlen(message));
    write(fd,message,strlen(message));
    write(fd,message,strlen(message));
    close(fd);
    return 0;
}

文件test.txt写入

我们可以使用

man 2 stat

stat是一个结构体,类型为struct stat,我们可以查到stat的结构体里面有

 1.1.1利用stat查看文件大小

我们现在主要使用它的st_size属性, 我们看代码:

#include<stdio.h>
#include<string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
const char * filename="test.txt";
int main()
{
    struct stat st;
    stat(filename,&st);
    printf("file size : %d\n",st.st_size);
    int fd=open(filename,O_CREAT|O_WRONLY|O_TRUNC,0666);
    if(fd<0)
    {
        perror("fopen\n");
        return 1;
    }
    printf("fd : %d \n",fd);
    // const char * message="hello Linux\n";
    // write(fd,message,strlen(message));
    // write(fd,message,strlen(message));
    // write(fd,message,strlen(message));
    close(fd);
    return 0;
}

运行结果为:

 1.1.2利用stat读取文件

        通过read进行文件读取,读取的个数就是文件的大小,代码如下:

#include<stdio.h>
#include<string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include<stdlib.h>
const char *filename="test.txt";
int main()
{
    struct stat st;
    stat(filename,&st);
    printf("file size : %d\n",st.st_size);
    int fd=open(filename,O_RDONLY);
    if(fd<0)
    {
        perror("fopen\n");
        return 1;
    }

    char* message=(char*)malloc(st.st_size+1);
    int n =read(fd,message,st.st_size);
    if(n>0)
    {
        message[st.st_size]='\0';
        printf("%s\n",message);
    }
    else 
    {
        perror("read");
        return 2;
    }
   
    close(fd);
    return 0;
}

1.2文件描述符分配规则

1.2.1分别关闭文件描述符

        文件描述符的分配规则就是按照从小到大进行分配,查询到一个最小的文件描述符,其中0,1,2分别是标准输入,标注输出,标注错误这三个,在这里重点不在这里,我尝试关闭0,1,2输出一些内容,可以看到什么,代码如下:

#include<stdio.h>
#include<string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include<stdlib.h>
const char *filename="test.txt";
int main()
{
    //close(0);
    //close(1);
    //close(2);
    printf("hello linux\n");
    return 0;
}

分别关闭0,1,2结果为:

 1.2.2只关闭1

        只关闭文件描述符1,再打开一个文件,那这个文件的文件描述符是1,文件描述符1是标准输出啊,先看代码:

#include<stdio.h>
#include<string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include<stdlib.h>
const char *filename="test.txt";
int main()
{
    close(1);
    
    int fd=open("test.txt",O_CREAT|O_WRONLY|O_TRUNC,0666);
    if(fd<0)
    {
        perror("open\n");
        return 1;
    }

    printf("printf fd: %d\n",fd);
    fprintf(stdout,"fprintf fd: %d\n",fd);

    fflush(stdout);
    close(fd);

    return 0;
}

再显示器中没有任何输出,它的输出在text.txt文件中

如果我们删除fflush(stdout)这句test.txt就不会有任何内容,下面图片可以大致表示此过程:

 关闭1,再打开一个文件,这个文件的文件描述符为1,由于上层不知道底层的变化,所以stdout的文件描述符还是1,文件描述符1对应着stdout,在stdout写入,会存储在它的语言级缓冲区,当程序结束会刷新这个缓冲区,但是我们文件关闭了所以当没有fflush就不会有内容,有fflush会将语言级缓存区内容刷新到内核级文件缓冲区中,然后再刷入磁盘。这个操作就是一种重定向。 

1.3dup2

查看dup2文档可以看到:

这个接口就是来处理上面的情况,它将一个文件描述符表的oldfd的内容拷贝到newfd的位置,实现重定向,代码如下:

#include<stdio.h>
#include<string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include<stdlib.h>
const char *filename="test.txt";
int main()
{
    int fd=open("test.txt",O_CREAT|O_WRONLY|O_TRUNC,0666);
    if(fd<0)
    {
        perror("open\n");
        return 1;
    }
   dup2(fd,1);

    printf("printf fd: %d\n",fd);
    fprintf(stdout,"fprintf fd: %d\n",fd);

    close(fd);

    return 0;
}

运行结果为: 

1.4标准错误重定向

        在c语言中输出错误信息可以通过perror进行输出,可以通过重定向将错误信息和打印信息进行分离,先看代码:

#include<stdio.h>

int main()
{
    perror("I am perror\n");
    perror("I am perror\n");
    perror("I am perror\n");
    printf("I am parentf\n");
    printf("I am parentf\n");
    printf("I am parentf\n");
    return 0;
}

直接运行可以看到:

 

输入下面指令:

./a.out 1>test.txt

结果如下: 

 

可以这样将错误信息进行分离,当然将1改为2会将错误信息重定向到test.txt中。如何向让这俩 重定向到不同外文件中输入指令:

./a.out 1>test1.txt 2>test2.txt

想让这俩重定向到同一个文件输入指令:

 ./a.out 1>test.txt 2>&1

二.缓冲区

2.1刷新策略

        缓冲区是什么?缓冲区就是一段内存空间,缓冲区可以提高使用者的效率,使用者只需要将内容写道缓冲区即可,其余的事情不需要使用者管了,操作系统会自动进行,缓冲区有用户级缓冲区,还有内核级缓冲区。缓冲区的刷新策略有三种,第一种立即刷新(fflush等接口);第二种按行刷新(显示器,主要是为了照顾使用者的使用习惯),第三种全刷新(普通文件)。

#include<stdio.h>
#include<string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include<stdlib.h>
int main()
{
    printf("hello print\n");
    fprintf(stdout,"hello fprint\n");

    const char*mes="hello write\n";
    write(1,mes,strlen(mes));

    fork();

    return 0;
}

当文件生成可执行程序myfile,输入下面指令:

./myfile > test.txt

查看test.txt内容

这是重定向到普通文件,普通文件刷新策略是全缓冲,而子进程会继承父进程的代码和数据,所以缓冲区了也会继承,所以会有两份,由于write是直接写入所以没有继承。

2.2封装一下库

#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#define SIZE 1024
struct my_FILE
{
    int _fileno;
    int _flag;
    char _buff[SIZE];
    int _pos;
    int _size;
};
typedef struct my_FILE my_FILE;
my_FILE* my_fopen(const char *pathname, const char *mode);
void my_fflush(my_FILE* fp);
size_t my_write(my_FILE* fp,const char* messag,size_t len);
int my_read(my_FILE* fp,char*data,int len);
void my_fclose(my_FILE *fp);
#include"mystdio.h"

my_FILE* my_fopen(const char *pathname, const char *mode)
{
    int fd=0;
    int flag=0;
    int creat=0;
    if(strcmp(mode,"w")==0)
    {
        flag=O_CREAT|O_WRONLY|O_TRUNC;
        creat=1;
    }
    else if(strcmp(mode,"a")==0)
    {
        flag=O_APPEND|O_CREAT|O_WRONLY;
        creat=1;
    }
    else if(strcmp(mode,"r")==0)
    {
        flag=O_RDONLY;
    }
    else 
    {
        return NULL;
    }

    if(creat)
    {
        fd=open(pathname,flag,0666);
    }
    else 
    {
        fd=open(pathname,flag);
    }
    if(fd<0) return NULL;
    my_FILE* fp= (my_FILE*)malloc(sizeof(my_FILE));
    fp->_fileno=fd;
    fp->_flag=1;
    fp->_pos=0;
    fp->_size=0;

    return fp;
}
void my_fflush(my_FILE* fp)
{
    write(fp->_fileno,fp->_buff,fp->_pos);
    fp->_pos=0;
}
size_t my_write(my_FILE* fp,const char* messag,size_t len)
{
    memcpy(fp->_buff+fp->_pos,messag,len);
    fp->_pos+=len; 
    fp->_size+=len;
    if(fp->_flag==1&&fp->_buff[fp->_pos-1]=='\n')
    {
        my_fflush(fp);
    }
   
    return fp->_pos;

}

int my_read(my_FILE* fp,char*data,int len)
{
   return read(fp->_fileno,data,len);
}

void my_fclose(my_FILE *fp)
{
    my_fflush(fp);
    close(fp->_fileno);
    free(fp);
}
#include"mystdio.h"

int main()
{
    my_FILE* fp=my_fopen("log.txt","w");
    if(fp==NULL)
    {
        perror("my_fopen\n");
        return 1;
    }
    // char* mes="hello file\n";
    // my_write(fp,mes,strlen(mes));
    // my_write(fp,mes,strlen(mes));
    // my_write(fp,mes,strlen(mes));
    // my_write(fp,mes,strlen(mes));
    // my_write(fp,mes,strlen(mes));
    // my_write(fp,mes,strlen(mes));
    // char mes[SIZE];
    // int n=my_read(fp,mes,sizeof(mes));
    // mes[n]='\0';
    // printf("%s\n",mes);
    my_fclose(fp);

    return 0;
}

可以进行文件的写入和读取。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

添加缓存的相关策略以及缓存的主动更新

目录 添加缓存 代码实现 主动更新缓存 先删缓存在操作数据库 数据一致 数据不一致 先操作数据库在删除缓存 数据一致 数据不一致 总结 添加缓存 代码实现 public Shop queryWithPassThtough(Long id){ //先从redis中查询缓存 String object stringRedisTemplate.opsFor…

《零散知识点 · SpringBoot Starter》

&#x1f4e2; 大家好&#xff0c;我是 【战神刘玉栋】&#xff0c;有10多年的研发经验&#xff0c;致力于前后端技术栈的知识沉淀和传播。 &#x1f497; &#x1f33b; CSDN入驻不久&#xff0c;希望大家多多支持&#xff0c;后续会继续提升文章质量&#xff0c;绝不滥竽充数…

开放式耳机音质上不如入耳式耳机?高性价比开放式耳机推荐大赏

这可不一定&#xff01; 开放式耳机在音质上并不一定就比传统的入耳式耳机差。 首先&#xff0c;音质的好坏不能仅仅根据耳机的类型来判断。开放式耳机在声音的开放性和自然度方面往往具有优势。它们能够提供更宽广的音场&#xff0c;让声音有一种自然扩散的感觉&#xff0c;使…

『 Linux 』线程同步问题与条件变量

文章目录 同步问题条件变量条件变量的使用条件变量的条件检查与线程唤醒生产者-消费者模型 同步问题 同步问题是保证数据安全的情况下,使多线程在访问同一资源时具有一定顺序性; #define NUM 5int g_val 700;class threadData {public:threadData(const int number, pthread_…

linux常使用的命令

关机命令 shutdown halt poweroff reboot grep 选项 参数 -l 显示所有包含关键字的文件名 -n 在匹配之前加上行号 -c 只显示匹配的行数 -v 显示不匹配的行 管道符 “|” 左边的输出作为右边的输入 例如&#xff1a;我们找个文件包含abc 但是不含有def的文件 grep …

Adobe Acrobat DC 2021版安装教程【超简单、超详细】

Adobe Acrobat DC 是 Adobe 提供的一款专业 PDF 解决方案&#xff0c;具有许多强大的功能&#xff0c;可以满足各种文档处理需求。 注意事项&#xff1a;①下载与激活过程中一定要关闭杀毒软件 ②提供的所有软件都是永久版的 ③软件仅供学习下载使用&#xff0c;不可用于商业用…

C++ 右值 左值引用

一.什么是左值引用 右值引用 1.左值引用 左值是一个表示数据的表达式(如变量名或解引用的指针)&#xff0c;我们可以获取它的地址可以对它赋值。定义时const修饰符后的左值&#xff0c;不能给他赋值&#xff0c;但是可以取它的地址。左值引用就是给左值的引用&#xff0c;给左…

环境如何搭建部署Nacos

这里我使用的是Centos7&#xff0c; Nacos 依赖 Java环境来运行。如果您是从代码开始构建并运行Nacos&#xff0c;还需要为此配置 Maven环境&#xff0c;请确保是在以下版本环境中安装使用 ## 1、下载安装JDK wget https://download.oracle.com/java/17/latest/jdk-17_linux-x6…

知识文库杂志知识文库杂志社知识文库编辑部2024年第12期目录

文艺理论 现代高校书院对中国传统书院学术精神的汲取与转化 李奥楠;时新洁; 1-4 个案工作介入高中美术艺考生及家长心理调适的应用研究 魏星; 5-8《知识文库》投稿&#xff1a;cn7kantougao163.com 中华优秀传统文化视角下高校美育课程实践教学 李丛丛; 9-12 基…

Pytorch GPU环境搭建-博客导航

这里写目录标题 安装安装VS(CUDA需要VS)安装CUDA安装CUDNN创建Pytorch GPU虚拟环境 测试疑难杂症解决链接搭建VGG分类网络并用CUDA训练使用CUDA加速推理分类网络C#使用ONNXruntime-gpu推理 安装 安装VS(CUDA需要VS) 2017&#xff0c;2019&#xff0c;2022都可 安装CUDA Cud…

山东润馨教育专家团队多次举办各种扶贫及公益讲座

一、山东润馨教育专家鲁书婉老师举办了以“发掘孩子的天赋潜能”为主题的公益讲座 在这个充满温情与希望的春日&#xff0c;3月16日&#xff0c;山东润馨教育专家团队带着满满的爱心与智慧&#xff0c;踏入了德州学院附属第一实验小学联合滨河社区&#xff0c;成功举办了一场以…

如何恢复硬盘里删除的数据?硬盘数据恢复真的可靠吗?2024最新解答!

在日常的计算机使用中&#xff0c;我们时常会不小心删除硬盘中的重要数据&#xff0c;这时候&#xff0c;数据恢复就显得尤为重要。本文将介绍几种恢复硬盘里删除数据的方法&#xff0c;并探讨硬盘数据恢复的可靠性&#xff0c;提供2024年的最新解答。 一、什么是电脑硬盘&…

【Linux】进程创建进程终止进程等待

目录 一、进程创建1.1 写时拷贝1.2 frok的常规用法1.3 fork调用失败的原因 二、进程终止2.1 进程退出码2.2 进程退出方式2.2.1 exit函数的使用2.2.2 _exit函数的使用2.2.3 exit函数与_exit函数的区别 2.3 进程信号 三、进程等待3.1 进程等待的必要性3.2 进程等待的方式3.2.1 wa…

从零开始的MicroPython(一) 软件安装及环境搭建

文章目录 MicroPython简介下载安装 ESP32(NodeMCU-32S)简介引脚注意事项 CH340下载安装 Thonny IDE下载 Python简介下载环境配置 MicroPython 简介 ​ MicroPython 是 Python 3 编程语言的精简高效的实现 其中包括 Python 标准库的一小部分&#xff0c;并且是经过优化&#x…

达梦数据库系列—40.执行计划

目录 优化器 执行计划 操作符 执行过程 优化器 查询优化器通过分析可用的执行方式和查询所涉及的对象统计信息来生成最优的执行计划。此外&#xff0c;如果存在 HINT 优化提示&#xff0c;优化器还需要考虑优化提示的因素。 查询优化器的处理过程包括&#xff1a; 1.优化…

手摸手教你撕碎西门子S7通讯协议14--开发自己的通讯库读数据

1、S7通讯回顾 - &#xff08;1&#xff09;建立TCP连接 Socket.Connect- - &#xff08;2&#xff09;发送访问请求 COTP- - &#xff08;3&#xff09;交换通信信息 Setup Communication- - &#xff08;4&#xff09;执行相关操作 读、写、PLC启停、时间…

【Android】DrawerLayout+NavigationView实现侧滑菜单页面

【Android】DrawerLayoutNavigationView实现侧滑菜单页面 在 Android 开发中&#xff0c;侧滑菜单是一个非常常见的用户界面模式&#xff0c;它能够在屏幕的一侧显示一个导航菜单&#xff0c;允许用户通过滑动手势或点击按钮来访问不同的应用功能。本文将介绍如何使用 DrawerL…

网页UI设计工具全攻略:九大精选

如果担心不知道如何进行网站 UI 设计、设计网站和编辑网页技术程序&#xff0c;很多人会选择快速方便的 Wix 建设。然而&#xff0c;如果你想建立一个最合适的网站&#xff0c;使用一个功能强大、资源丰富的网站 UI 设计工具仍然是您的最佳选择。网站设计中的 UI 设计不同于一般…

你是否知道Vue的data两种不同定义区别呢?

在做vue项目的时候&#xff0c;虽然vue3出来了一段时间了&#xff0c;vue2已经官方宣布不再维护了&#xff0c;然而我们有些旧项目原来是用的vue2的&#xff0c;那么用了那么久的vue2&#xff0c;不知道你是否有注意到&#xff0c;vue2我们往往会在根文件定义了一个对象形式的d…