流量对于网站盈利,沈阳黑酷做网站建设优化公司怎么样,wordpress文章设置,建设银行英文网站目录
0. 文件先前理解
1. C文件接口
1.1 写文件
1.2 读文件
1.3 输出信息到显示器
1.4 总结 and stdin stdout stderr
2. 系统调用文件I/O
2.1 系统接口使用示例
2.2 接口介绍
2.3 open函数返回值
3. 文件描述符fd及重定向
3.1 0 1 2
3.2… 目录
0. 文件先前理解
1. C文件接口
1.1 写文件
1.2 读文件
1.3 输出信息到显示器
1.4 总结 and stdin stdout stderr
2. 系统调用文件I/O
2.1 系统接口使用示例
2.2 接口介绍
2.3 open函数返回值
3. 文件描述符fd及重定向
3.1 0 1 2
3.2 文件描述符fd的理解
3.3 文件描述符Linux内核源码分析
3.4 文件描述符的分配规则
3.5 重定向
3.6 使用 dup2 系统调用
4. 在minishell中添加重定向功能
5. 有关FILE缓冲区及Linux一切皆文件理解
5.1 一切皆文件及C语言实现运行时多态
5.2 缓冲区
5.3 C标准库FILE结构体及缓冲区
5.4 大致模拟缓冲区设计 0. 文件先前理解 文件 文件内容 文件属性 因此对文件进行的操作无非对内容对属性 文件本质存放在磁盘上访问文件先写代码 - 编译 - exe - 运行 -访问文件其本质是进程在访问文件 要向硬件写入只有操作系统具备权利而普通用户写入则需要使用操作系统提供的文件类系统调用接口而语言层面对系统调用接口进行了封装由于不同操作系统平台提供的系统调用接口不同语言把所有平台的代码都实现条件编译—动态裁剪使其语言具有跨平台性如果语言不提供对文件的系统接口的封装所有访问文件的操作都必须使用OS的接口一旦使用系统接口编写所谓的文件代码就无法在其他平台安全运行就不再具有跨平台性。 学习OS层面的文件系统本质是为了更好的理解不同语言底层操作实际是相通的 显示器是硬件而printf向显示器打印本质也是一种写入 Linux下一切皆文件又该如何理解 文件而言 曾经理解的文件站在写程序的角度将文件打开加载到内存进行readwrite 显示器printfcout - 一种write 键盘scanfcin - 一种read 普通文件 - fopen/fread - 你的进程内部内存 - fwrite - 文件中 input output 站在系统的角度能够被input读取或者output写出的设备就叫做文件 狭义的文件——普通磁盘文件 广义上的文件——显示器键盘网卡声卡显卡磁盘。几乎所有外色都可以称之为文件 1. C文件接口 打开文件fopen 打开方式 rrwwaa... fopen以当前路径打开文件或者相对路径打开文件 当前路径默认是当一个进程运行起来在其pcb结构体中没个进程都会记录当前所处的工作路径—cwd创建工作的目录即为该进程的当前路径 1.1 写文件 //打开文件 FILE * fopen ( const char * filename, const char * mode ); //关闭文件 int fclose ( FILE * stream ); //fwrite: size_t fwrite( const void *buffer, size_t size, size_t count, FILE *stream ); buffer—要写入的数据 size—一个元素的大小 count—几个元素 stream—文件流 #include stdio.h
#include string.h
int main()
{FILE* fp fopen(myfile, w);if (!fp) {printf(fopen error!\n);}const char* msg hello Linux!\n;int count 5;while (count--) {fwrite(msg, strlen(msg), 1, fp);}fclose(fp);return 0;
} 注意这里写入字符串不计算\0因为字符串结束标志是C语言提供的而不是操作系统所具有的 1.2 读文件 //fread: size_t fread( void *buffer, size_t size, size_t count, FILE *stream ); #include stdio.h
#include string.h
int main()
{FILE* fp fopen(myfile, r);if (!fp) {printf(fopen error!\n);}char buf[1024];const char* msg hello Linux!\n;while (1) {size_t s fread(buf, 1, strlen(msg), fp);if (s 0) {buf[s] 0;printf(%s, buf);}if (feof(fp)) {break;}}fclose(fp);return 0;
} 1.3 输出信息到显示器 将写入的数据流入stdout即输出到显示器 #include stdio.h
#include string.h
int main()
{const char* msg hello fwrite\n;fwrite(msg, strlen(msg), 1, stdout);printf(hello printf\n);fprintf(stdout, hello fprintf\n);return 0;
} 1.4 总结 and stdin stdout stderr C默认会打开三个输入输出流分别是stdin, stdout, stderr 仔细观察发现这三个流的类型都是FILE*, fopen返回值类型文件指针 打开文件的方式 r Open text file for reading. The stream is positioned at the beginning of the file. r Open for reading and writing. The stream is positioned at the beginning of the file. w Truncate(缩短) file to zero length or create text file for writing. The stream is positioned at the beginning of the file. w Open for reading and writing.The file is created if it does not exist, otherwise it is truncated. The stream is positioned at the beginning of the file. a Open for appending (writing at end of file). The file is created if it does not exist. The stream is positioned at the end of the file. a Open for reading and appending (writing at end of file). The file is created if it does not exist. The initial file position for reading is at the beginning of the file, but output is always appended to the end of the file. 如上是我们之前学的文件相关操作。还有 fseek ftell rewind 的函数在C部分已经有所涉猎请同学们自 行复习。 2. 系统调用文件I/O 操作文件除了上述C接口当然C也有接口其他语言也有我们还可以采用系统接口来进行文件访问 先来直接以代码的形式实现和上面一模一样的代码 2.1 系统接口使用示例 open close read write O_CREAT O_TRUNC O_WRONLY O_RDONLY O_RDWR... 写文件 #include stdio.h
#include sys/types.h
#include sys/stat.h
#include fcntl.h
#include unistd.h
#include string.h
int main()
{umask(0);int fd open(myfile, O_WRONLY | O_CREAT, 0644);if (fd 0) {perror(open);return 1;}int count 5;const char* msg hello Linux!\n;int len strlen(msg);while (count--) {write(fd, msg, len);//fd: 后续 msg缓冲区首地址 len: 本次读取期望写入多少个字节的数//据。 返回值实际写了多少字节数据}close(fd);return 0;
} 读文件 #include stdio.h
#include sys/types.h
#include sys/stat.h
#include fcntl.h
#include unistd.h
#include string.h
int main()
{int fd open(myfile, O_RDONLY);if (fd 0) {perror(open);return 1;}const char* msg hello Linux!\n;char buf[1024];while (1) {ssize_t s read(fd, buf, strlen(msg));//类比writeif (s 0) {printf(%s, buf);}else {break;}}close(fd);return 0;
} 在应用层看到一个很简单的动作在系统接口层面甚至OS层面可能要做非常多的动作 2.2 接口介绍 open —— man open #includesys/types.h
#includesys/stat.h
#includefcntl.h int open(const char *pathname, int flags); int open(const char *pathname, int flags, mode_t mode);
pathname: 要打开或创建的目标文件
flags: 打开文件时可以传入多个参数选项用下面的一个或者多个常量进行“或”运算构成flags。 参数: O_RDONLY: 只读打开 O_WRONLY: 只写打开 O_RDWR : 读写打开 这三个常量必须指定一个且只能指定一个 O_CREAT : 若文件不存在则创建它。需要使用mode选项来指明新文件的访问权限 O_APPEND: 追加写
返回值成功新打开的文件描述符失败-1 mode_t理解直接 man 手册比什么都清楚。 open 函数具体使用哪个和具体应用场景相关如目标文件不存在需要open创建则第三个参数表示创建文件的默认权限,否则使用两个参数的open。 2.3 open函数返回值 在认识返回值之前先来认识一下两个概念: 系统调用 和 库函数 上面的 fopen fclose fread fwrite 都是C标准库当中的函数我们称之为库函数libc而 open close read write lseek 都属于系统提供的接口称之为系统调用接口操作系统概念时画的一张图 系统调用接口和库函数的关系一目了然。 所以可以认为f#系列的函数都是对系统调用的封装方便二次开发。 3. 文件描述符fd及重定向 由上述系统调用open可知成功返回新打开的文件描述符file descriptorfp #include stdio.h
#include sys/types.h
#include sys/stat.h
#include fcntl.h
#include unistd.h
#include string.h
int main()
{//未做差错处理int fd1 open(myfile1, o_rdonly);int fd2 open(myfile2, o_rdonly);int fd3 open(myfile3, o_rdonly);int fd4 open(myfile4, o_rdonly);printf(open sucess, fd: %d, fd1);printf(open sucess, fd: %d, fd2);printf(open sucess, fd: %d, fd3);printf(open sucess, fd: %d, fd4);close(fd1);close(fd2);close(fd3);close(fd4);return 0;
} 而打开多个文件发现其fd从3开始依次递增0,1,2去哪里了 可知文件描述符就是一个小整数如何理解文件描述符 3.1 0 1 2 Linux进程默认情况下会有3个缺省打开的文件描述符分别是标准输入0 标准输出1 标准错误2.0,1,2对应的物理设备一般是键盘显示器显示器所以输入输出还可以采用如下方式 #include stdio.h
#include sys/types.h
#include sys/stat.h
#include fcntl.h
#include string.h
int main()
{char buf[1024];ssize_t s read(0, buf, sizeof(buf));if (s 0) {buf[s] 0;write(1, buf, strlen(buf));write(2, buf, strlen(buf));}return 0;
} 3.2 文件描述符fd的理解 FILE* fopenconst char* path const char* mode C标准库提供的文件操作可知FILE是一个由C标准库提供的结构体 由上述可知C语言库函数内部一定封装了系统调用接口lib在系统调用之上站在系统角度操作系统并不认识C语言内部的FILE结构体只认识fd因此FILE结构体内部必定封装了fd 所以fd是什么 进程想要访问文件必须先打开文件 一般而言进程 打开文件 1 n 文件要被访问前提是加载到内存中才能直接被访问 可知一个进程可以打开多个文件多个进程运行时会存在大量的被打开的文件所以操作系统需要把这些被各个进程打开的文件管理起来如何管理先描述在组织 而现在知道文件描述符就是从0开始的小整数。当我们打开文件时操作系统在内存中要创建相应的数据结构来描述目标文件。于是就有了file结构体。表示一个已经打开的文件对象。而进程执行open系统调用所以必须让进 程和文件关联起来。每个进程都有一个指针*files, 指向一张表files_struct,该表最重要的部分就是包涵一个指针数组每个元素都是一个指向打开文件的指针所以本质上文件描述符就是该数组的下标。所以只要拿着文件描述符就可以找到对应的文件 3.3 文件描述符Linux内核源码分析 文件磁盘文件未被打开的文件 内存文件被进程打开的文件 因此没每个进程的PCB中都一个文件表指针struct files_struct * files指向一个struct files_struct文件表结构体该文件表结构体内一定包含一个struct file* fd_array[]文件指针数组其数组每个对应存取指向一个被打开文件结构体file的指针file记录了该文件的所有属性及所有内容。 fwrite() - FILE* - fd - write - write(fd, ...) - 自己执行操作系统内部的write方法 - 能找到进程的task_struct - *files - files_struct - fd_array[] - fd_array[fd] - struct file - 内存文件被找到 - 操作 #includestdio.h
#includestdlib.hstruct File {void(*read_p)();void(*write_p)();
};void readByKeyBoard() {cout 从键盘读取 endl;
}
void writeByKeyBoard() {cout nothing to do! endl;
}void readByFile() {cout 从文件读取 endl;
}
void writeFile() {cout 往文件写入 endl;
}void TestFile(struct File file) {file.read_p();file.write_p();
}int main() {struct File fileByKB;fileByKB.read_p readByKeyBoard;fileByKB.write_p writeByKeyBoard;struct File fileByFILE;fileByFILE.read_p readByFile;fileByFILE.write_p writeFile;TestFile(fileByFILE);TestFile(fileByKB);return 0;
}每个文件被加载到内存其文件管理创建对应的file结构体结构体内部会根据驱动填充相应的函数指针writeread等操作函数的地址因为磁盘、显示器、键盘、网卡等不用硬件的读写方法不同因此为了实现多态调用C语言可以采用函数指针实例化对象时填充不同的函数地址以实现多态调用 3.4 文件描述符的分配规则 直接看代码 #include stdio.h
#include sys/types.h
#include sys/stat.h
#include fcntl.h
int main()
{int fd open(myfile, O_RDONLY);if(fd 0){perror(open);return 1;}printf(fd: %d\n, fd);close(fd);return 0;
}输出发现是 fd: 3 关闭0或者2在看 #include stdio.h
#include sys/types.h
#include sys/stat.h
#include fcntl.h
int main()
{close(0);//close(2);int fd open(myfile, O_RDONLY);if (fd 0) {perror(open);return 1;}printf(fd: %d\n, fd);close(fd);return 0;
}发现是结果是 fd: 0 或者 fd 2 可见文件描述符的分配规则在files_struct数组当中找到当前没有被使用的最小的一个下标作为新的文件描述符。 3.5 重定向 那如果关闭1呢看代码 #include stdio.h
#include sys/types.h
#include sys/stat.h
#include fcntl.h
#include stdlib.h
int main()
{close(1);int fd open(myfile, O_WRONLY | O_CREAT, 00644);if (fd 0) {perror(open);return 1;}printf(fd: %d\n, fd);fflush(stdout);close(fd);exit(0);
} 此时发现本来应该输出到显示器上的内容输出到了文件 myfile 当中其中fd1。这种现象叫做输出重定向。常见的重定向有:, , 重定向的本质其实是在OS内部更改fd对应的内容的指向 3.6 使用 dup2 系统调用 函数原型如下: #includeunistd.h int dup2(int oldfd, int newfd); 这里的oldfd copy 给 newfd必要时会释放oldfd 代码如下 #include stdio.h
#include unistd.h
#include fcntl.h
int main() {int fd open(./log, O_CREAT | O_RDWR);if (fd 0) {perror(open);return 1;}close(1);dup2(fd, 1);for (;;) {char buf[1024] { 0 };ssize_t read_size read(0, buf, sizeof(buf) - 1);if (read_size 0) {perror(read);break;}printf(%s, buf);fflush(stdout);}return 0;
} 4. 在minishell中添加重定向功能 #includestdio.h
#includestdlib.h
#includestring.h
#includeunistd.h
#includesys/wait.h
#includesys/types.h
#includesys/stat.h
#includefcntl.h
#includeassert.h#define NUM 1024
#define SIZE 32
char cmd_line[NUM];void dealStr(char* str, char** argv) {char* dealstr NULL;const char* sep ;size_t i 0;for (dealstr strtok(str, sep); dealstr ! NULL; dealstr strtok(NULL, sep)) {if (i SIZE) {argv[i] dealstr;i;}}if(strcmp(argv[0], ls) 0){argv[i] (char*)--colorauto;}argv[i] NULL;
}#define INPUT_REDIR 1
#define OUTPUT_REDIR 2
#define APPEND_REDIR 3
#define NONE_REDIR 0
int redir_status NONE_REDIR;char* CheckRedir(char* start){assert(start);//ls -a -l\0char* end start strlen(start) - 1;while(end start){if(*end ){if(*(end - 1) ){redir_status APPEND_REDIR;*(end-1) \0;end;break;}redir_status OUTPUT_REDIR;*end \0;end;break;//ls -a -lmyfile.txt//ls -a -lmyfile.txt}else if(*end ){redir_status INPUT_REDIR;*end \0;end;break;}else{end--;}}if(end start){return end;//要打开文件}else{return NULL;}
}//环境变量保存的是地址而拿不到环境变量本质是地址内容被清空,MY_VALNULL
char buffer[64];//shell运行原理通过让子进程执行命令父进程等待解析命令
int main(){//extern char** environ;while(1){//1.打印提示信息printf([rootlocalhost myshell]#);fflush(stdout);//2.获取用户输入memset(cmd_line, \0, sizeof cmd_line);char *g_argv[SIZE] { NULL };if(fgets(cmd_line, sizeof cmd_line, stdin) NULL){continue;}cmd_line[strlen(cmd_line) - 1] \0;//printf(echo:%s\n, cmd_line);//ls -a -l log.txt - ls -a -l\0log.txtchar* sep CheckRedir(cmd_line);//3.命令行字符串分割dealStr(cmd_line, g_argv); //4.TODO 内置命令让父进程(shell)自己执行的命令叫做内置命令内建命令// 内建命令本质就是shell中的一个函数调用if(strcmp(cd, g_argv[0]) 0){//not child execute, father executeif(g_argv[1] ! NULL)chdir(g_argv[1]);continue;}if(strcmp(export, g_argv[0]) 0 g_argv[1] ! NULL){strcpy(buffer, g_argv[1]);putenv(buffer);//int checkPutenv putenv(buffer);//if(checkPutenv 0){// printf(Putenv sucess\n);// //for(size_t i 0; environ[i]; i){// //printf(%s\n, environ[i]);// // }//}else{// printf(Putenc fail\n);//}continue;}//5.forkpid_t id fork();if(id 0){perror(fork);exit(1);}else if(id 0){if(sep ! NULL){int fd -1;switch(redir_status){case INPUT_REDIR:fd open(sep, O_RDONLY);dup2(fd, 0);break;case OUTPUT_REDIR:fd open(sep, O_WRONLY | O_TRUNC | O_CREAT, 0666);dup2(fd, 1);break;case APPEND_REDIR:fd open(sep, O_WRONLY | O_APPEND);dup2(fd, 1);break;default:printf(bug?\n);break;}}//printf(getenv : %s\n, getenv(MY_VAL));//execvpe(g_argv[0], g_argv, environ);//子进程继承父进程环境变量execvp(g_argv[0], g_argv);exit(1);}int status 0;pid_t ret waitpid(id, status, 0);if(ret 0){printf(exit code:%d - result:%s\n,WEXITSTATUS(status), strerror(WEXITSTATUS(status)));}else{printf(wait fail!\n);exit(1);}}//end whilereturn 0;
} 5. 有关FILE缓冲区及Linux一切皆文件理解 Linux设计则学——一切皆文件——体现在操作系统的软件设计层面的 5.1 一切皆文件及C语言实现运行时多态 Linux是C语言写的如何用C语言实现面向对象甚至是运行时多态 可以使用结构体及函数指针实现面向对象及运行时多态 在Linux下根据冯诺依曼体系结构大部分硬件都输入IO设备IO设备主要用于输入输出因此根据其驱动层提供的读写方法即可实现一切皆文件的概念 所有的底层设备都可以有自己的read和write方法的具体实现但是其具体的代码一定是不一样的而通过函数指针使其根据硬件指向不同的读写方法在调用时就像在使用同一个函数 5.2 缓冲区 1. 什么是缓冲区 就是一段内存空间这个空间社提供 用户char buffer[64], scanf(buffer)? 语言 OS 2. 为什么要有缓冲区 提高整机效率主要是为了提高用户的响应速度 写透模式WT模式 只要写就刷新会进行频繁的IO操作成本高且效率慢 回写模式WB模式提供缓冲区定义刷新策略IO操作次数降低效率高 缓冲区的刷新策略 1. 立即刷新 2. 行刷新行缓冲 3. 满刷新全缓冲 特殊情况 1. 用户强制刷新fflush 2. 进程退出 缓冲策略 一般 特殊 缓冲策略是可控制的 一般而言行缓冲设备文件 —— 显示器 全缓冲设备文件 —— 磁盘文件 所有的设备永远都倾向于全缓冲 —— 缓冲区满了才刷新 -- 需要更少次的IO操作 -- 更少次的外设访问 -- 提高效率 和外部设备进行IO的时候数据量的大小不是主要矛盾而和外设预备IO的过程是最耗费时间的其他刷新策略是结合具体情况所做的妥协 显示器需要直接给用户看的一方面照顾效率一方面照顾用户体验极端情况是可以自定义规则的 5.3 C标准库FILE结构体及缓冲区 因为IO相关函数与系统调用接口对应并且库函数封装系统调用所以本质上访问文件都是通过fd访问的。 所以C库当中的FILE结构体内部必定封装了fd。 来段代码在研究一下 #include stdio.h
#include string.h
int main()
{const char* msg0 hello printf\n;const char* msg1 hello fwrite\n;const char* msg2 hello write\n;printf(%s, msg0);fwrite(msg1, strlen(msg0), 1, stdout);write(1, msg2, strlen(msg2));fork();return 0;
} 运行出结果 hello printf
hello fwrite
hello write 但如果对进程实现输出重定向呢 ./hello file 我们发现结果变成了 hello write
hello printf
hello fwrite
hello printf
hello fwrite 我们发现 printf 和 fwrite 库函数都输出了2次而 write 只输出了一次系统调用。为什么呢肯定和 fork有关 一般C库函数写入文件时是全缓冲的而写入显示器是行缓冲。printf fwrite 库函数会自带缓冲区进度条例子就可以说明当发生重定向到普通文件时数据 的缓冲方式由行缓冲变成了全缓冲。而我们放在缓冲区中的数据就不会被立即刷新甚至fork之后但是进程退出之后会统一刷新写入文件当中。但是fork的时候父子数据会发生写时拷贝所以当你父进程准备刷新的时候子进程也就有了同样的 一份数据随即产生两份数据。write 没有变化说明没有所谓的缓冲。 综上 printf fwrite 库函数会自带缓冲区而 write 系统调用没有带缓冲区。另外我们这里所说的缓冲区 都是用户级缓冲区。其实为了提升整机性能OS也会提供相关内核级缓冲区不过不再我们讨论范围之内。 那这个缓冲区谁提供呢 printf fwrite 是库函数 write 是系统调用库函数在系统调用的“上层” 是对系统 调用的“封装”但是 write 没有缓冲区而 printf fwrite 有足以说明该缓冲区是二次加上的又因为是 C所以由C标准库提供。 如果有兴趣可以看看FILE结构体: 在 / usr / include / libio.h
struct _IO_FILE {int _flags; /* High-order word is _IO_MAGIC; rest is flags. */
#define _IO_file_flags _flags//缓冲区相关/* The following pointers correspond to the C streambuf protocol. *//* Note: Tk uses the _IO_read_ptr and _IO_read_end fields directly. */char* _IO_read_ptr; /* Current read pointer */char* _IO_read_end; /* End of get area. */char* _IO_read_base; /* Start of putbackget area. */char* _IO_write_base; /* Start of put area. */char* _IO_write_ptr; /* Current put pointer. */char* _IO_write_end; /* End of put area. */char* _IO_buf_base; /* Start of reserve area. */char* _IO_buf_end; /* End of reserve area. *//* The following fields are used to support backing up and undo. */char* _IO_save_base; /* Pointer to start of non-current get area. */char* _IO_backup_base; /* Pointer to first valid character of backup area */char* _IO_save_end; /* Pointer to end of non-current get area. */struct _IO_marker* _markers;struct _IO_FILE* _chain;int _fileno; //封装的文件描述符
#if 0int _blksize;
#elseint _flags2;
#endif_IO_off_t _old_offset; /* This used to be _offset but its too small. */
#define __HAVE_COLUMN /* temporary *//* 1column number of pbase(); 0 is unknown. */unsigned short _cur_column;signed char _vtable_offset;char _shortbuf[1];/* char* _save_gptr; char* _save_egptr; */_IO_lock_t* _lock;
#ifdef _IO_USE_OLD_IO_FILE
}; 5.4 大致模拟缓冲区设计 #includestdio.h
#includestring.h
#includeunistd.h
#includesys/types.h
#includesys/stat.h
#includefcntl.h
#includeassert.h
#includestdlib.h#define NUM 1024
typedef struct MyFILE_{int fd;char buffer[NUM];int end;//当前缓冲区结尾
}MFILE;MFILE* fopen_(const char* pathname, const char* mode){assert(pathname mode);MFILE* fp NULL;if(strcmp(mode, r) 0){}else if(strcmp(mode, r) 0){}else if(strcmp(mode, w) 0){int fd open(pathname, O_WRONLY | O_CREAT | O_TRUNC, 0666);if(fd 0){fp (MFILE*)malloc(sizeof(MFILE));memset(fp, 0, sizeof(MFILE));fp-fd fd;}}else if(strcmp(mode, w) 0){}else if(strcmp(mode, a) 0){}else if(strcmp(mode, a) 0){}else{}return fp;
}void fputs_(const char* message, MFILE* fp){assert(message fp);strcpy(fp-buffer fp-end, message);fp-end strlen(message);//for debugfprintf(stderr, %s\n, fp-buffer);//暂时未刷新//制定刷新策略刷新策略是由谁来执行的呢//答案是用户通过执行C标准库中的代码逻辑来完成刷新//效率提高体现几行代码就减少频繁的IO操作执行次数(注不是数据量)//stdinif(fp-fd 0){//stdout}else if(fp-fd 1){if(fp-buffer[fp-end-1] \n){
// //for debug
// fprintf(stderr, fllush : %s, fp-buffer);write(fp-fd, fp-buffer, fp-end);fp-end 0;}//stderr}else if(fp-fd 2){}else{}
}void fflush_(MFILE* fp){assert(fp);if(fp-end ! 0){//暂且认为刷新了 -- 其实是把数据写到了内核中//如果想将数据由内核刷新到外设系统调用sync-syncfswrite(fp-fd, fp-buffer, fp-end);syncfs(fp-fd);//将数据写入磁盘fp-end 0;}
}void fclose_(MFILE* fp){assert(fp);fflush_(fp);close(fp-fd);free(fp);
}
int main(){//close(1);MFILE* fp fopen_(./log.txt, w);if(fp NULL){printf(open file error\n);return 1;}fputs_(one:hello world\n, fp);//fputs_(two:hello world\n, fp);//fputs_(three:hello world, fp);//fputs_(four:hello world\n, fp);//fputs_(five:hello world, fp);//行刷新策略 one one:two tree tree:four fivefork();//fork时上下文数据写时拷贝在缓冲区的one刷新两次fclose_(fp);return 0;
}