Linux中的文件缓冲区

目录

使用 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;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值