Linux中的文件描述符

发布于:2024-05-20 ⋅ 阅读:(154) ⋅ 点赞:(0)

1.系统调用接口和库函数的关系

  1. 函数:fopen fclose fread fwrite 都是c标准库当中的函数,也就是用户操作接口中ibc
  2. 系统调用:open close read write 都是系统调用提供的接口

c语言中接口底层封装的都是系统调用接口

FILE* stdin stdout stderr  都是c语言封装的结构体,必定封装特定文件描述符fd

2.文件描述符fd

文件描述符fd(File Descriptor) 是一个用于表示打开文件、套接字、管道或设备等资源的整数。在Linux中,所有执行I/O操作的系统调用都通过文件描述符来完成。

文件描述符的主要用途是允许程序对文件进行各种操作,如读写、关闭等。在程序刚启动的时候,默认有三个文件描述符,分别是:

  • 0:标准输入(stdin)
  • 1:标准输出(stdout)
  • 2:标准错误(stderr)

这些文件描述符都是预定义的,可以直接使用。

在Linux系统中,文件描述符是一个非负整数,用于指代被打开的文件。所有执行I/O操作的系统调用都通过文件描述符来完成。程序刚刚启动的时候,默认有三个文件描述符,分别是0(代表标准输入),1(代表标准输出),2(代表标准错误)。如果此时去打开一个新的文件,它的文件描述符会是3。

当我们创建文件时,操作系统要创建相应的数据结构来描述目标文件,结构体file就表示一个已经打开的文件对象。进程要调用open打开文件,需要通过PCB(task_struct)中的file*,找到指向的一张表file_struct,这个表最重要有一个指针数组,每一个指针指向file。本质上,文件描述符就是这个数组的下标,拿着文件描述符就可以找到对应的文件。 


Linux下一切皆文件 

struct file中除了上面的变量之外,其实还有函数指针,指向一个一个硬件的读(read)写(write)方法,通过结构体file就可以调用硬件,所以在Linux下一切皆文件。

3.dup2系统调用 

dup2 是一个在 Linux 系统中常用的系统调用,用于复制一个已存在的文件描述符,使其引用另一个文件描述符。如果目标文件描述符(第二个参数)已经打开,那么 dup2 会先关闭它,然后再进行复制操作。

  • 如果oldfd不是有效的文件描述符,调用失败,不关闭newfd
  • 如果oldfd是有效的文件描述符,newfd是和oldfd一样的,然后dup2()什么都不做,并且返回newfd 

本质就是用oldfd覆盖newfd

4. 模拟c语言的文件函数

c语言中当调用fwirte/printf/fputs函数时,其实是写入c语言自带的缓冲区,当遇到换行或者自带的缓冲区满了就会调用write系统接口,再写入文件的缓冲区。在减少fwrite时,减少调用write的调用,聚集数据一次性拷贝,调高整体效率,有效的提高c的使用效率。

缓冲区就是一块内存空间,本质就是用空间换取时间

  • 定义了一个结构体MyFILE,定义字符数组表示缓冲区,pos存储了多少数据,cap表示最大容量,fileno表示文件描述符,flushmode表示刷新模式(NONE_FLUSHLINE_FLUSHFULL_FLUS)。
  • my_open函数用于打开文件,设置一下掩码为0(umask的解释在这篇文章),通过调用open返回fd,然后为MyFILE动态开辟空间。
  • my_fflush函数用于刷新缓冲区,把缓冲区的数据通过系统调用写入文件的缓冲区。内核会在适当的时候(例如,当缓冲区满时,或者当显式请求刷新缓冲区时)将数据从内核缓冲区写入磁盘。
  • my_write函数把数据写入缓冲区中
#include <stdio.h>
#include <string.h>

#define MAXCAP 1024
#define NONE_FLUSH (1<<1)//不缓冲
#define LINE_FLUSH (1<<2)//行缓冲 
#define FULL_FLUSH (1<<3) //全缓冲


typedef struct MyFILE
{
    char buffer[MAXCAP];//缓冲区
    int pos;//存储了多少数据
    int cap;//容量是多少
    int fileno;
    int flushmode;//刷新模式

}MyFILE;


MyFILE* my_fopen(const char *path, const char *mode)
{

    umask(0);
    int fd = -1;
    if(strcmp(mode,"r")==0)
        fd = open(path,O_RDONLY);
    else if(strcmp(mode,"w")==0)
        fd = open(path,O_WRONLY|O_TRUNC|O_CREAT,0666);
    else if(strcmp(mode,"a")==0)
        fd = open(path,O_WRONLY|O_APPEND|O_CREAT,0666);
    else    
        return NULL;

    MyFILE* fp = (MyFILE*)malloc(sizeof(MyFILE));
    if(fp==NULL)    return NULL;
    fp->fileno = fd;
    fp->pos = 0;
    fp->cap = MAXCAP;
    fp->flushmode = LINE_FLUSH;

    return fp;
}

void my_fflush(MyFILE* fp)
{
    if(fp->pos==0)  return;
    write(fp->fileno,fp->buffer,fp->pos);
    fp->pos = 0;

}

size_t my_fwrite(const void *ptr, size_t size,MyFILE* fp)
{
    memcpy(fp->buffer,ptr,size);
    fp->pos += size;//每次写入增加size长度
    
    if((fp->flushmode & LINE_FLUSH) && fp->buffer[fp->pos-1] == '\n')
        my_fflush(fp);
    else if((fp->flushmode&LINE_FLUSH) && fp->pos == fp->cap)
        my_fflush(fp);

    return size;

}

void my_fclose(MyFILE *fp)
{
    close(fp->fileno);
    free(fp); 
}


网站公告

今日签到

点亮在社区的每一天
去签到