Linux中的文件缓冲区

news2024/11/18 5:36:16

目录

使用 dup2 系统调用

为命令行解释器添加重定向功能

理解缓冲区问题

缓存区的刷新策略

FILE的本质

尝试封装C语言的FILE


小共识:

cd->当前路径->当前进程的工作路径->工作路径可以被修改->每个进程都有当前路径->故cd改的是子进程的路径->修改父进程的路径需要系统调用

fd的分配规则:从小到大,按照顺序从struct file* fd_array[]寻找最小且没有被占用的fd

重定向的本质:

重定向的本质:上层用的fd不变,在内核中更改fd对应的struct file*的地址。

使用 dup2 系统调用

#include <unistd.h>
 
int dup2(int oldfd, int newfd);

将newfd中内容拷贝到oldfd.

将向显示器打印的内容重定向到log.txt.

为命令行解释器添加重定向功能

#define NONE_REDIR 0
#define INPUT_REDIR 1
#define OUTPUT_REDIR 2
#define APPEND_REDIR 3
#define NUM 1024
#define OPT_NUM 64
#define trimSpace(start) do{\
    while(isspace(*start)) ++start;\
}while(0)

char lineCommand[NUM];
char* myargv[OPT_NUM];
int lastCode=0;
int lastSig=0;
int redirType=NONE_REDIR;
char* redirFile=NULL;
void comandCheck(char* commands)
{
    assert(commands);
    char* start=commands;
    char* end=commands+strlen(commands);
    while (start<end)
    {
        if(*start=='>')
        {
            *start='\0';
            start++;
            if(*start=='>')
            {
                //"ls -a >> file.log"
                redirType=APPEND_REDIR;
                start++;
            }
            else
            {
                //"ls -a> file.log"
                redirType=OUTPUT_REDIR;
            }
            trimSpace(start);
            redirFile=start;
            break;
        }
        else if(*start=='<')
        {
            //"cat < file.txt"
            *start='\0';
            start++;
            trimSpace(start);
            redirType=INPUT_REDIR;
            redirFile=start;
            break;
        }
        else
        {
            start++;
        }
    }
}

进程具有独立性,所以子进程会拷贝一份父进程的files_struct二者相互独立。而文件部分二者共享(上图中的虚线部分)。程序替换时不影响PCB及PCB内部的细节。

一个被打开的文件中包含引用计数,父进程或子进程关闭文件其本质是让引用计数--。

理解缓冲区问题

缓存区的刷新策略

a.立即刷新-无缓冲

b.行刷新---显示器(键盘)

c.缓冲区满---全缓存----磁盘文件

两种特殊情况

1.用户强制刷新---(fflush)

2.进程退出----通常情况下会进行缓冲区的刷新。

例如:

printf("hello printf\n");
    fprintf(stdout,"hello printf\n");
    const char* fputsString="hello fputs\n";
    fputs(fputsString,stdout);
    const char* wstring="hello write\n";
    write(1,wstring,strlen(wstring));
    fork();

c语言的库函数被打印了两次而系统接口write只被打印了一次。这是为什么????????

在代码结束之前创建子进程

1.如果没有进行> 可以看到4条消息,stdout默认使用的是行刷新,在进程fork之前,C函数已经被打印输出到显示器上(外设),FIEL内部,进程内部就没有对应的数据了

2.如果进行>,写入文件不在是显示器,而是普通文件,采用的刷新策略是全缓冲,三条C函数虽然带了'\n’, 但是不足以将整个缓存区写满,!!!!!!所以数据没有被刷新。

执行fork()函数时,stdout属于父进程,创建子进程时,创建之后的操作就是退出!!先退出的进程要进行缓存区的刷新(即为修改),这时会发生写时拷贝,最终数据会显示两份(父子进程最终都会去找缓冲区,然后将数据进行刷新)。

3.而write为什么没有呢??? write没有FILE,而是用的fd,故没有C提供的缓冲区。

FILE的本质

>log.txt

清空文件

尝试封装C语言的FILE

#pragma once
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<assert.h>
#include<string.h>
#include<errno.h>
#include<ctype.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<sys/wait.h>

#define SIZE 1024
#define SYNC_NOW 1
#define SYNC_LINE 2
#define SYNC_FULL 4

typedef struct _FILE
{
    int flags;//刷新方式
    int fileno;
    int cap;//buffer的总容量
    int size;//buffer当前容量
    char buffer[SIZE];
}FILE_;

FILE_* fopen_(const char* path_name,const char* mode);
void fwrite_(const void* ptr,int num,FILE_* fp);
void fclose_(FILE_* fp);
void fflush_(FILE_* fp);
FILE_* fopen_(const char* path_name,const char* mode)
{
    int flags=0;
    int defaultMode=0666;
    if(strcmp(mode,"r")==0)
    {
        flags|=O_RDONLY;
    }
    else if(strcmp(mode,"w")==0)
    {
        flags|=(O_WRONLY|O_CREAT|O_TRUNC);
    }
    else if(strcmp(mode,"a")==0)
    {
        flags|=(O_WRONLY|O_CREAT|O_APPEND);
    }
    else
    {
        //..
    }
    int fd=0;
    if(flags&O_RDONLY) fd=open(path_name,flags);
    else fd=open(path_name,flags,defaultMode);
    if(fd<0)
    {
        const char* err=strerror(errno);
        write(2,err,strlen(err));
        return NULL;
    }
    FILE_* fp=(FILE_*)malloc(sizeof(FILE_));
    assert(fp);
    fp->flags=SYNC_LINE;//默认是行刷新
    fp->fileno=fd;
    fp->cap=SIZE;
    fp->size=0;
    memset(fp->buffer,0,SIZE);
    return fp;
}
void fwrite_(const void* ptr,int num,FILE_* fp)
{
    //1.写入到缓冲区中
    memcpy(fp->buffer+fp->size,ptr,num);
    fp->size+=num;
    //2.判断是否刷新
    if(fp->flags&SYNC_NOW)
    {
        write(fp->fileno,fp->buffer,fp->size);
        fp->size=0;
    }
    else if(fp->flags&SYNC_FULL)
    {
        if(fp->size==fp->cap)
        {
            write(fp->fileno,fp->buffer,fp->size);
            fp->size=0;
        }
    }
    else if(fp->flags&SYNC_LINE)
    {
        if(fp->buffer[fp->size-1]=='\n')
        {
            write(fp->fileno,fp->buffer,fp->size);
            fp->size=0;
        }
    }
    else
    {

    }
}
void fflush_(FILE_* fp){
    if(fp->size>0) write(fp->fileno,fp->buffer,fp->size);

}
void fclose_(FILE_* fp)
{
    fflush_(fp);
    close(fp->fileno);
}

故数据被写到硬件上至少会进过三次拷贝。

void fflush_(FILE_* fp){
    if(fp->size>0) write(fp->fileno,fp->buffer,fp->size);
    fsync(fp->fileno);//将数据,强制要求os进行外设刷新
    fp->size=0;
}

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

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

相关文章

算力共享平台的控制流程,业务流程

目录 控制流程 业务流程 在提供的计算机网络系统结构示意图和描述中,我们可以区分出控制流程和业务流程的组成部分。 控制流程 控制流程主要涉及系统内部的管理、调度和监控操作,以确保系统能够按照预定的规则和策略运行。在这个例子中,控制流程可能包括但不限于以下部分…

K8S真正删除pod

假设k8s的某个命名空间如&#xff08;default&#xff09;有一个运行nginx 的pod&#xff0c;而这个pod是以kubectl run pod命令运行的 1.错误示范&#xff1a; kubectl delete pod nginx-2756690723-hllbp 结果显示这个pod 是删除了&#xff0c;但k8s很快自动创建新的pod,但是…

【C++算法】5.双指针_乘最多水的容器

文章目录 题目链接&#xff1a;题目描述&#xff1a;解法C 算法代码&#xff1a;图解&#xff1a; 题目链接&#xff1a; 11.盛最多水的容器 题目描述&#xff1a; 解法 7x749 解法一&#xff1a;暴力枚举 输入&#xff1a;[1&#xff0c;8&#xff0c;6&#xff0c;2&#xf…

大厂AI必备数据结构与算法——链表(三)详细文档

冲冲冲&#xff01;开干 神马&#xff01;神马&#xff01;神马&#xff0c;一向让我们学习起来抓耳挠腮的数据结构课程竟然也有教程&#xff1f;还那么详细&#xff1f;&#xff1f;真的假的&#xff1f; 那么好&#xff0c;胡广告诉你是假的&#xff0c;哈哈哈哈哈哈哈哈哈…

付费计量系统通用处理类(下)

普通处理类如下定义&#xff1a; 10.11 Class 11: Customer _Delivery process 用户交付过程 The processes associated with transferring delivered electrical energy to the customer’s load circuit. 是关于将传输的电能传递到用户的负荷电路。 Examples…

告别熬夜,追求高效写作:芝士AI写作,效率与质量的双重提升

好的工具&#xff0c;真得能够让我们的学习事半功倍&#xff0c;有了芝士AI&#xff08;paperzz&#xff09;工具的加持&#xff0c;妈妈再也不用担心我熬夜写论文了 。 芝士AI官网&#xff1a;https://www.paperzz.cn/ 不愧是由985硕博团队开发的AI大模型功软件&#xff0c;…

LeetCode 427. 建立四叉树

LeetCode 427. 建立四叉树 &#xff08;题干略&#xff09; """ # Definition for a QuadTree node. class Node:def __init__(self, val, isLeaf, topLeft, topRight, bottomLeft, bottomRight):self.val valself.isLeaf isLeafself.topLeft topLeftself.t…

04_OpenCV图片缩放

import cv2 import matplotlib.pyplot as plt # Python 的 2D绘图库# 读入原图片 img cv2.imread(libarary.JPG) # 打印出图片尺寸 print(img.shape) # 将图片高和宽分别赋值给x&#xff0c;y x, y img.shape[0:2]# 显示原图 #cv.imshow(OriginalPicture, img)# 缩放到原来的…

模拟实战数据落地:MSsql通过存储过程获得销售数据视图

话不多说 目标需求:通过传递参数(查询条件及查询时间)调用存储过程获得销售数据视图,并且在视图中有时间字段供后续引用,实现数据对接获取任务 最终结果如图: 实现以上结果步骤如下: 1)建立users表和orders表分别代表用户及订单,其中订单中用户id与用户表中用户id关联,并随机…

C++初阶:STL详解(九)——stacke和queue的模拟实现

✨✨小新课堂开课了&#xff0c;欢迎欢迎~✨✨ &#x1f388;&#x1f388;养成好习惯&#xff0c;先赞后看哦~&#x1f388;&#x1f388; 所属专栏&#xff1a;C&#xff1a;由浅入深篇 小新的主页&#xff1a;编程版小新-CSDN博客 前言&#xff1a; 我们已将了解了 stack和q…

PHP爬虫:获取商品销量详情API的利器

在电子商务时代&#xff0c;商品的销量数据对于商家来说至关重要。它不仅能够帮助商家了解市场动态&#xff0c;还能够指导库存管理和营销策略。PHP作为一种流行的服务器端脚本语言&#xff0c;结合其强大的HTTP请求处理能力&#xff0c;可以有效地用于编写爬虫程序&#xff0c…

【教学类-18-04】20240508《蒙德里安“黑白格子画” 七款图案挑选》

背景需求 最近有2位客户买了蒙德里安黑白格子画的素材&#xff0c;其中一位问是否是1000张。 【教学类-18-03】20240508《蒙德里安“红黄蓝黑格子画”-A4横版》&#xff08;大小格子&#xff09;_processing简单图形画蒙德里安-CSDN博客文章浏览阅读1.1k次&#xff0c;点赞35次…

记忆osi七层模型的口诀/方法/谐音

物理层、数据链路层、网络层、传输层、会话层、表示层和应用层 物数网传会表应&#xff08;无数网船会飙英&#xff08;语&#xff09;&#xff09; 记忆方法&#xff1a; 想象面前有很多挂着渔网的船&#xff0c;船上的人会说英语。 无数的网船上会有人飙英语。

【Android】多角度看handler--looper的阻塞

在【Android】app中阻塞的looper为什么可以响应touch事件_消息队列阻塞为什么还能响应点击事件-CSDN博客 里面&#xff0c;我们查看到input事件唤醒应用中的looper阻塞&#xff0c; 作为对比&#xff0c;我们再看看广播中的唤醒&#xff0c;我们知道&#xff0c;在注册的广播…

大数据毕业设计选题推荐-食品销售数据分析系统-Hive-Hadoop-Spark

✨作者主页&#xff1a;IT研究室✨ 个人简介&#xff1a;曾从事计算机专业培训教学&#xff0c;擅长Java、Python、微信小程序、Golang、安卓Android等项目实战。接项目定制开发、代码讲解、答辩教学、文档编写、降重等。 ☑文末获取源码☑ 精彩专栏推荐⬇⬇⬇ Java项目 Python…

RabbitMQ的高级特性-延迟队列

延迟队列(Delayed Queue)&#xff0c;即消息被发送以后, 并不想让消费者⽴刻拿到消息, ⽽是等待特定时间后,消费者才能拿到这个消息进⾏消费 应用场景 延迟队列的使⽤场景有很多, ⽐如: 1. 智能家居: ⽤⼾希望通过⼿机远程遥控家⾥的智能设备在指定的时间进⾏⼯作. 这时候就可…

linux项目_c语言:Makefile编写、动态库生成、添加动态库路径

一直想搞懂Linux中Makefile是怎么管理项目的&#xff0c;知识积累到一定程度后&#xff0c;我就做了一个自己的缩小项目去把剩下的细节搞清楚 代码&#xff1a; Service.c: #include <stdio.h> #include "lib_sevr.h" int main(){printf("输入a, b的值…

【C++】内存管理:内存分布、new/delete

本篇主要介绍一下C的内存管理相关知识。C的内存管理和C语言保持一致。 1.C/C内存分布 一个程序的数据存储是需要分区的。常见的内存区域划分如下。 我们学C主要了解栈&#xff0c;堆&#xff0c;数据段&#xff0c;代码段 。 我们先看下面代码和相关问题。 int globalvar…

Python编码系列—Python命令模式:将请求封装为对象

&#x1f31f;&#x1f31f; 欢迎来到我的技术小筑&#xff0c;一个专为技术探索者打造的交流空间。在这里&#xff0c;我们不仅分享代码的智慧&#xff0c;还探讨技术的深度与广度。无论您是资深开发者还是技术新手&#xff0c;这里都有一片属于您的天空。让我们在知识的海洋中…

RabbitMQ——消息的可靠性处理

1.业务分析 在业务的开发中&#xff0c;我们通常将业务的非核心业务交给MQ来处理&#xff0c;比如支付&#xff0c;在支付过后&#xff0c;我们需要扣减余额&#xff0c;修改支付单状态&#xff0c;修改订单状态&#xff0c;发送短信提醒用户&#xff0c;给用户增加积分等等&am…