1.系统调用接口和库函数的关系
- 函数:fopen fclose fread fwrite 都是c标准库当中的函数,也就是用户操作接口中ibc
- 系统调用: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_FLUSH,LINE_FLUSH,FULL_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);
}