【Linux:文件】基础IO:文件操作的系统调用和库函数各个接口汇总及代码演示
🎬 艾莉丝的简介:myfile.clog.txtmyfile.cloga.txt3 文件操作3:理解缓冲区Print.c4 文件操作4:实现stdioMakefilemain.cmy_stdio.hmy_stdio.c结尾uu们,本文的内容到这里就全部结束了,艾莉丝在这里再次感谢您的阅读! 结语:希望对学习Linux相关内容的uu有所帮助,不要忘记给博主“一键四连”哦!往期回顾:【Linux进程
·
《Linux操作系统编程详解》《笔试/面试常见算法:从基础到进阶》《Python干货分享》
🎬 艾莉丝的简介:

文章目录

1 ~> 文件操作的系统调用和库函数各个接口
1.1 C语言文件操作常用接口
| 函数名 | 功能 | 头文件 | 参数说明 |
|---|---|---|---|
fopen |
打开文件 | stdio.h | FILE* fopen(const char* filename, const char* mode) |
fclose |
关闭文件 | stdio.h | int fclose(FILE* stream) |
fputc |
写入一个字符 | stdio.h | int fputc(int char, FILE* stream) |
fgetc |
读取一个字符 | stdio.h | int fgetc(FILE* stream) |
fputs |
写入一个字符串 | stdio.h | int fputs(const char* str, FILE* stream) |
fgets |
读取一个字符串 | stdio.h | char* fgets(char* str, int n, FILE* stream) |
fprintf |
格式化写入数据 | stdio.h | int fprintf(FILE* stream, const char* format, ...) |
fscanf |
格式化读取数据 | stdio.h | int fscanf(FILE* stream, const char* format, ...) |
fwrite |
向二进制文件写入数据 | stdio.h | size_t fwrite(const void* ptr, size_t size, size_t nmemb, FILE* stream) |
fread |
从二进制文件读取数据 | stdio.h | size_t fread(void* ptr, size_t size, size_t nmemb, FILE* stream) |
fseek |
设置文件指针的位置 | stdio.h | int fseek(FILE* stream, long int offset, int whence) |
ftell |
计算当前文件指针相对于起始位置的偏移量 | stdio.h | long int ftell(FILE* stream) |
rewind |
设置文件指针到文件的起始位置 | stdio.h | void rewind(FILE* stream) |
ferror |
判断文件操作过程中是否发生错误 | stdio.h | int ferror(FILE* stream) |
feof |
判断文件指针是否读取到文件末尾 | stdio.h | int feof(FILE* stream) |
1.2 系统调用和库函数汇总
1.2.1 系统调用
| 系统调用 | 函数功能 | 头文件 | 参数模板 | 返回值 |
|---|---|---|---|---|
| open() | 打开或创建文件 | <fcntl.h> |
int open(const char *pathname, int flags, mode_t mode); |
文件描述符(成功)/-1(失败) |
| creat() | 创建新文件 | <fcntl.h> |
int creat(const char *pathname, mode_t mode); |
文件描述符(成功)/-1(失败) |
| close() | 关闭文件描述符 | <unistd.h> |
int close(int fd); |
0(成功)/-1(失败) |
| read() | 从文件描述符读取数据 | <unistd.h> |
ssize_t read(int fd, void *buf, size_t count); |
读取字节数/-1(失败) |
| write() | 向文件描述符写入数据 | <unistd.h> |
ssize_t write(int fd, const void *buf, size_t count); |
写入字节数/-1(失败) |
| lseek() | 移动文件读写指针 | <unistd.h> |
off_t lseek(int fd, off_t offset, int whence); |
新偏移量/-1(失败) |
| pread() | 从指定偏移处读取 | <unistd.h> |
ssize_t pread(int fd, void *buf, size_t count, off_t offset); |
读取字节数/-1(失败) |
| pwrite() | 向指定偏移处写入 | <unistd.h> |
ssize_t pwrite(int fd, const void *buf, size_t count, off_t offset); |
写入字节数/-1(失败) |
| fsync() | 将文件数据同步到磁盘 | <unistd.h> |
int fsync(int fd); |
0(成功)/-1(失败) |
| fdatasync() | 同步文件数据(不包括元数据) | <unistd.h> |
int fdatasync(int fd); |
0(成功)/-1(失败) |
| ftruncate() | 截断/扩展文件大小 | <unistd.h> |
int ftruncate(int fd, off_t length); |
0(成功)/-1(失败) |
| dup() | 复制文件描述符 | <unistd.h> |
int dup(int oldfd); |
新文件描述符/-1(失败) |
| dup2() | 复制文件描述符到指定值 | <unistd.h> |
int dup2(int oldfd, int newfd); |
新文件描述符/-1(失败) |
| fcntl() | 文件描述符控制操作 | <fcntl.h> |
int fcntl(int fd, int cmd, ... /* arg */); |
依赖cmd/-1(失败) |
| ioctl() | 设备I/O控制 | <sys/ioctl.h> |
int ioctl(int fd, unsigned long request, ...); |
依赖request/-1(失败) |
| mmap() | 内存映射文件 | <sys/mman.h> |
void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset); |
映射地址/MAP_FAILED |
| munmap() | 解除内存映射 | <sys/mman.h> |
int munmap(void *addr, size_t length); |
0(成功)/-1(失败) |
| mprotect() | 修改内存保护属性 | <sys/mman.h> |
int mprotect(void *addr, size_t len, int prot); |
0(成功)/-1(失败) |
| msync() | 同步内存映射到文件 | <sys/mman.h> |
int msync(void *addr, size_t length, int flags); |
0(成功)/-1(失败) |
| stat() | 获取文件状态信息 | <sys/stat.h> |
int stat(const char *pathname, struct stat *statbuf); |
0(成功)/-1(失败) |
| fstat() | 通过fd获取文件状态 | <sys/stat.h> |
int fstat(int fd, struct stat *statbuf); |
0(成功)/-1(失败) |
| lstat() | 获取符号链接状态 | <sys/stat.h> |
int lstat(const char *pathname, struct stat *statbuf); |
0(成功)/-1(失败) |
| access() | 检查文件访问权限 | <unistd.h> |
int access(const char *pathname, int mode); |
0(成功)/-1(失败) |
| chmod() | 修改文件权限 | <sys/stat.h> |
int chmod(const char *pathname, mode_t mode); |
0(成功)/-1(失败) |
| fchmod() | 通过fd修改文件权限 | <sys/stat.h> |
int fchmod(int fd, mode_t mode); |
0(成功)/-1(失败) |
| chown() | 修改文件所有者 | <unistd.h> |
int chown(const char *pathname, uid_t owner, gid_t group); |
0(成功)/-1(失败) |
| fchown() | 通过fd修改文件所有者 | <unistd.h> |
int fchown(int fd, uid_t owner, gid_t group); |
0(成功)/-1(失败) |
| readlink() | 读取符号链接目标 | <unistd.h> |
ssize_t readlink(const char *pathname, char *buf, size_t bufsiz); |
读取字节数/-1(失败) |
| rename() | 重命名/移动文件 | <stdio.h> |
int rename(const char *oldpath, const char *newpath); |
0(成功)/-1(失败) |
| mkdir() | 创建目录 | <sys/stat.h> |
int mkdir(const char *pathname, mode_t mode); |
0(成功)/-1(失败) |
| rmdir() | 删除空目录 | <unistd.h> |
int rmdir(const char *pathname); |
0(成功)/-1(失败) |
1.2.2 C语言标准库文件IO函数
| 库函数 | 函数功能 | 头文件 | 参数模板 | 返回值 |
|---|---|---|---|---|
| fopen() | 打开文件流 | <stdio.h> |
FILE *fopen(const char *path, const char *mode); |
文件指针/NULL |
| freopen() | 重新打开文件流 | <stdio.h> |
FILE *freopen(const char *path, const char *mode, FILE *stream); |
文件指针/NULL |
| fclose() | 关闭文件流 | <stdio.h> |
int fclose(FILE *stream); |
0(成功)/EOF |
| fread() | 从文件流读取数据 | <stdio.h> |
size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream); |
读取元素数 |
| fwrite() | 向文件流写入数据 | <stdio.h> |
size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream); |
写入元素数 |
| fgetc() | 从文件流读取字符 | <stdio.h> |
int fgetc(FILE *stream); |
字符/EOF |
| getc() | 从文件流读取字符 | <stdio.h> |
int getc(FILE *stream); |
字符/EOF |
| getchar() | 从标准输入读取字符 | <stdio.h> |
int getchar(void); |
字符/EOF |
| fputc() | 向文件流写入字符 | <stdio.h> |
int fputc(int c, FILE *stream); |
字符/EOF |
| putc() | 向文件流写入字符 | <stdio.h> |
int putc(int c, FILE *stream); |
字符/EOF |
| putchar() | 向标准输出写入字符 | <stdio.h> |
int putchar(int c); |
字符/EOF |
| fgets() | 从文件流读取字符串 | <stdio.h> |
char *fgets(char *s, int size, FILE *stream); |
字符串指针/NULL |
| fputs() | 向文件流写入字符串 | <stdio.h> |
int fputs(const char *s, FILE *stream); |
非负数/EOF |
| fseek() | 移动文件流指针 | <stdio.h> |
int fseek(FILE *stream, long offset, int whence); |
0(成功)/-1 |
| ftell() | 获取文件流当前位置 | <stdio.h> |
long ftell(FILE *stream); |
当前位置/-1 |
| rewind() | 重置文件流到开头 | <stdio.h> |
void rewind(FILE *stream); |
无 |
| fgetpos() | 获取文件流位置 | <stdio.h> |
int fgetpos(FILE *stream, fpos_t *pos); |
0(成功)/非0 |
| fsetpos() | 设置文件流位置 | <stdio.h> |
int fsetpos(FILE *stream, const fpos_t *pos); |
0(成功)/非0 |
| feof() | 检测文件结束标志 | <stdio.h> |
int feof(FILE *stream); |
非0(结束)/0 |
| ferror() | 检测文件错误标志 | <stdio.h> |
int ferror(FILE *stream); |
非0(错误)/0 |
| clearerr() | 清除错误和EOF标志 | <stdio.h> |
void clearerr(FILE *stream); |
无 |
| fflush() | 刷新文件流缓冲区 | <stdio.h> |
int fflush(FILE *stream); |
0(成功)/EOF |
| setvbuf() | 设置文件流缓冲模式 | <stdio.h> |
int setvbuf(FILE *stream, char *buf, int mode, size_t size); |
0(成功)/非0 |
| setbuf() | 设置文件流缓冲区 | <stdio.h> |
void setbuf(FILE *stream, char *buf); |
无 |
| fprintf() | 格式化输出到文件流 | <stdio.h> |
int fprintf(FILE *stream, const char *format, ...); |
输出字符数/负数 |
| printf() | 格式化输出到标准输出 | <stdio.h> |
int printf(const char *format, ...); |
输出字符数/负数 |
| sprintf() | 格式化输出到字符串 | <stdio.h> |
int sprintf(char *str, const char *format, ...); |
输出字符数/负数 |
| fscanf() | 从文件流格式化输入 | <stdio.h> |
int fscanf(FILE *stream, const char *format, ...); |
匹配项数/EOF |
| scanf() | 从标准输入格式化输入 | <stdio.h> |
int scanf(const char *format, ...); |
匹配项数/EOF |
| sscanf() | 从字符串格式化输入 | <stdio.h> |
int sscanf(const char *str, const char *format, ...); |
匹配项数/EOF |
| tmpfile() | 创建临时文件 | <stdio.h> |
FILE *tmpfile(void); |
文件指针/NULL |
| tmpnam() | 生成临时文件名 | <stdio.h> |
char *tmpnam(char *s); |
文件名指针/NULL |
| remove() | 删除文件 | <stdio.h> |
int remove(const char *pathname); |
0(成功)/-1 |
| rename() | 重命名文件 | <stdio.h> |
int rename(const char *oldpath, const char *newpath); |
0(成功)/-1 |
| fileno() | 获取文件描述符 | <stdio.h> |
int fileno(FILE *stream); |
文件描述符/-1 |
| fdopen() | 从文件描述符创建文件流 | <stdio.h> |
FILE *fdopen(int fd, const char *mode); |
文件指针/NULL |
2 ~> 文件IO部分代码演示
2.1 文件操作1:打开文件方式
Makefile
myfile:myfile.c
gcc -o $@ $^
.PHONY:clean
clean:
rm -f myfile
myfile.c
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main()
{
umask(0); // 就近原则
int fd = open("log.txt",O_CREAT | O_WRONLY | O_TRUNC,0666); // OS本身自带的系统调用,相当于w
if(fd < 0) // 打开文件失败
{
perror("open");
return 1;
}
//const char *message = "1234567890abcdefg\n";
const char *message = "ccc";
write(fd,message,strlen(message));
close(fd);
return 0;
}
//#define ONE (1<<0) // 1
//#define TWO (1<<1) // 2
//#define THREE (1<<2) // 4
//#define FOUR (1<<3) // 8
//#define FIVE (1<<4) // 16
//
//void Print(int flags)
//{
// if(flags & ONE)
// printf("ONE\n");
// if(flags & TWO)
// printf("TWO\n");
// if(flags & THREE)
// printf("THREE\n");
// if(flags & FOUR)
// printf("FOUR\n");
// if(flags & FIVE)
// printf("FIVE\n");
//}
//
//int main()
//{
// Print(ONE);
// printf("\n");
// Print(TWO);
// printf("\n");
// Print(ONE | TWO);
// printf("\n");
// Print(ONE | TWO | THREE);
// printf("\n");
// Print(ONE | TWO | THREE | FOUR);
// printf("\n");
// Print(TWO | THREE | FOUR | FIVE);
//}
//int main()
//{
// //chdir("/home/Alice/118/linux-git"); // 可以修改文件新建的路径
// //char pwd[64];
// //getcwd(pwd, sizeof(pwd));
// //printf("cwd: %s\n", pwd);
//
//
// FILE *fp = fopen("log.txt", "w");
// if(NULL == fp)
// {
// perror("fopen");
// return 0;
// }
//
// char inbuffer[1024];
// while(1)
// {
// long pos = ftell(fp);
// printf("pos: %ld\n", pos);
// int ch = fgetc(fp);
// if(ch == EOF)
// {
// break;
// }
// printf("%c\n", ch);
// //if(!fgets(inbuffer, sizeof(inbuffer), fp))
// //{
// // break;
// //}
// printf("file : %s", inbuffer);
// }
//
// //const char *message = "abcd\n";
// //fputs(message, fp);
// ////int cnt = 0;
// //while(cnt < 10)
// //{
// // //fwrite(message, 1, strlen(message) + 1, fp); // 文件的字符串这里不用+1,+1即算上了\0(C语言)
// // fputs(message, fp);
// // //fprintf(fp, "hello Alice: %d\n", cnt);
// // cnt++;
// //}
//
// fclose(fp);
// return 0;
//}
log.txt

2.2 文件操作2:重定向
Makefile
myfile:myfile.c
gcc -o $@ $^
.PHONY:clean
clean:
rm -f myfile
myfile.c
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main()
{
//int fda = open("loga.txt",O_RDONLY);
//dup2(fda,0);
//int a = 0;
//float f = 0.0f;
//char c = 0;
//scanf("%d %f %c",&a,&f,&c);
//printf("%d %f %c\n",a,f,c);
//// 联系重定向
//// 格式化输出:上层的用户层,stdout -> 1
//printf("hello printf\n"); // stdout -> 1
//fprintf(stdout,"hello fprintf\n"); // stdout -> 1
//// 三个标准流,默认打开的
//printf("stdin: %d\n",stdin->_fileno);
//printf("stdout: %d\n",stdout->_fileno);
//printf("stdout: %d\n",stderr->_fileno);
FILE *fp = fopen("log.txt","w");
printf("log.txt: %d\n",fp->_fileno); // 3(fd数组下标)
fprintf(stdout,"hello stdout\n");
close(1);
int fda = open("loga.txt",O_CREAT | O_WRONLY | O_TRUNC,0666);
int fdb = open("logb.txt",O_CREAT | O_WRONLY | O_TRUNC,0666);
int fdc = open("logc.txt",O_CREAT | O_WRONLY | O_TRUNC,0666);
fprintf(stdout,"fda : %d\n",fda);
fprintf(stdout,"fdb : %d\n",fdb);
fprintf(stdout,"fdc : %d\n",fdc);
//printf(stdout,"fda : %d\n",fda);
//printf(stdout,"fdb : %d\n",fdb);
//printf(stdout,"fdc : %d\n",fdc);
close(fda);
close(fdb);
close(fdc);
return 0;
}
//// cat file.txt
//int main(int argc,char *argv[])
//{
// if(argc != 2) // 没有第二个参数
// {
// printf("Ussage: %s filename\n",argv[0]); // ./myfile filename
// return 1;
// }
// int fd = open(argv[1],O_RDONLY); // 只读
// if(fd < 0) // 读错了
// {
// perror("open");
// return 2;
// }
//
// char inbuffer[128];
// while(1)
// {
// ssize_t n = read(fd,inbuffer,sizeof(inbuffer)-1); // 预备留给\n
// if(n > 0)
// {
// inbuffer[n] = 0;
// printf("%s",inbuffer);
// }
// else if(n == 0)
// {
// printf("end of file!\n"); // 到达文件结尾
// break;
// }
// else
// {
// perror("read");
// break;
// }
// }
//
// close(fd);
// return 0;
//}
//// 追加
//int main()
//{
// umask(0);
// int fd = open("log.txt",O_CREAT | O_WRONLY | O_APPEND,0666);
// if(fd < 0)
// {
// perror("open");
// return 1;
// }
//
// const char *msg = "hello Alice\n";
// int cnt = 10;
// while(cnt--)
// {
// write(fd,msg,strlen(msg));
// }
//
// close(fd);
// return 0;
//}
//// 写入
//int main()
//{
// umask(0); // 就近原则
// int fd = open("log.txt",O_CREAT | O_WRONLY | O_TRUNC,0666); // OS本身自带的系统调用,相当于w
// if(fd < 0) // 打开文件失败
// {
// perror("open");
// return 1;
// }
// //const char *message = "1234567890abcdefg\n";
// const char *message = "ccc";
// write(fd,message,strlen(message));
//
// close(fd);
// return 0;
//}
//#define ONE (1<<0) // 1
//#define TWO (1<<1) // 2
//#define THREE (1<<2) // 4
//#define FOUR (1<<3) // 8
//#define FIVE (1<<4) // 16
//
//void Print(int flags)
//{
// if(flags & ONE)
// printf("ONE\n");
// if(flags & TWO)
// printf("TWO\n");
// if(flags & THREE)
// printf("THREE\n");
// if(flags & FOUR)
// printf("FOUR\n");
// if(flags & FIVE)
// printf("FIVE\n");
//}
//
//int main()
//{
// Print(ONE);
// printf("\n");
// Print(TWO);
// printf("\n");
// Print(ONE | TWO);
// printf("\n");
// Print(ONE | TWO | THREE);
// printf("\n");
// Print(ONE | TWO | THREE | FOUR);
// printf("\n");
// Print(TWO | THREE | FOUR | FIVE);
//}
//int main()
//{
// //chdir("/home/Alice/118/linux-git"); // 可以修改文件新建的路径
// //char pwd[64];
// //getcwd(pwd, sizeof(pwd));
// //printf("cwd: %s\n", pwd);
//
//
// FILE *fp = fopen("log.txt", "w");
// if(NULL == fp)
// {
// perror("fopen");
// return 0;
// }
//
// char inbuffer[1024];
// while(1)
// {
// long pos = ftell(fp);
// printf("pos: %ld\n", pos);
// int ch = fgetc(fp);
// if(ch == EOF)
// {
// break;
// }
// printf("%c\n", ch);
// //if(!fgets(inbuffer, sizeof(inbuffer), fp))
// //{
// // break;
// //}
// printf("file : %s", inbuffer);
// }
//
// //const char *message = "abcd\n";
// //fputs(message, fp);
// ////int cnt = 0;
// //while(cnt < 10)
// //{
// // //fwrite(message, 1, strlen(message) + 1, fp); // 文件的字符串这里不用+1,+1即算上了\0(C语言)
// // fputs(message, fp);
// // //fprintf(fp, "hello Alice: %d\n", cnt);
// // cnt++;
// //}
//
// fclose(fp);
// return 0;
//}
loga.txt
fda : 1
fdb : 4
fdc : 5
2.3 文件操作3:理解缓冲区
Print.c
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/fcntl.h>
int main()
{
printf("hell world"); // 语言缓冲区 -> stdout->outbuffer
sleep(40); // 测试杀死这个进程
//// C库函数
//printf("hello printf\n");
//fprintf(stdout,"hello fprintf\n");
//const char *msg1 = "hello fputs\n";
//fputs(msg1,stdout);
// // 系统调用
// const char *msg2 = "hello write\n";
// write(1,msg2,strlen(msg2));
//fork(); // 最后
return 0;
}
//int main()
//{
// fprintf(stdout,"hello stdout\n");
// fprintf(stderr,"hello error\n");
//
// const char *msg1 = "hello 1\n";
// const char *msg2 = "hello 2\n";
// write(1,msg1,strlen(msg1));
// write(2,msg2,strlen(msg2));
//
// return 0;
//}
2.4 文件操作4:实现stdio
Makefile
testlibc:main.c my_stdio.c
gcc -o $@ $^
.PHONY:clean
clean:
rm -f testlibc
main.c
#include "my_stdio.h"
#include <string.h>
#include <unistd.h>
int main()
{
My_FILE *fp = Myfopen("log.txt","a"); // 相当于把这个log.txt当显示器了
if(!fp) // 打开文件失败或者底层malloc失败
return 1;
int cnt = 10;
//const char *msg = "hello world \n";
const char *msg = "hello world ";
while(cnt--)
{
Myfwrite(msg,1,strlen(msg),fp); // 模拟缓存的行为
Myfflush(fp);
sleep(2);
}
Myfclose(fp);
return 0;
}
my_stdio.h
#pragma once
typedef struct{
int fd;
int flags;
int mode; // 刷新策略
char outbuffer[1024];
int cap; // 缓冲区总容量
int size; // 缓冲区当前的数据量
//char inbuffer[1024];
}My_FILE;
// 定义宏,是什么类型的缓冲
#define NONE_CACHE 1
#define LINE_CACHE 2
#define FULL_CACHE 4 // 1 2 4只有一个比特位为1(0001/0010/0100),检测起来比较容易
My_FILE *Myfopen(const char *pathname,const char *mode); // r w a方式 --> 打开文件
//int Myfputs(const char *message,My_FILE *fp); // Myfputs可能太偏字符串,一会儿还要刷新,刷新还要写入,就用Myfwrite了
int Myfwrite(const char *message,int size,int num,My_FILE *fp); // 写文件
void Myfflush(My_FILE *fp); // 刷新
void Myfclose(My_FILE *fp); // 关闭文件
my_stdio.c
#include "my_stdio.h"
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <stdlib.h> // malloc
#include <unistd.h>
static mode_t global_mode = 0666;
// My_FILE *fp = Myfopen("log.txt","a");
// 我写的和库的基本的细节肯定不一样,但是核心思想在大的轮廓上是一样的
My_FILE *Myfopen(const char *pathname,const char *mode) // r w a方式(在内部选择不同的模式)
{
if(pathname == NULL || mode == NULL)
return NULL;
umask(0);
int fd = 0;
int flags = 0;
if(strcmp(mode,"w") == 0)
{
flags = O_CREAT | O_WRONLY | O_TRUNC;
fd = open(pathname,flags,global_mode);
(void)fd;
}
if(strcmp(mode,"r") == 0)
{
flags = O_RDONLY;
fd = open(pathname,flags); // 传两参数的open
(void)fd;
}
if(strcmp(mode,"a") == 0)
{
flags = O_CREAT | O_WRONLY | O_APPEND;
fd = open(pathname,flags,global_mode);
(void)fd;
}
else
{}
if(fd < 0) // 文件打开失败
return NULL;
// 创建My_FILE对象
My_FILE* fp = (My_FILE*)malloc(sizeof(My_FILE));
if(!fp) // 声明失败,直接返回
return NULL;
fp->fd = fd;
fp->flags = flags;
fp->mode = LINE_CACHE;
fp->cap = 1024; // 容量,因为缓冲区大小定义的就是1024,这一块也可以定义成宏
fp->size = 0; // 当前缓冲区里没有数据,size是0
//fp->outbuffer[0] = '\0'; // 我们把它当做字符串做的,我们这里默认这样就清空了,如果受不了这样写,可以像下面这样写
memset(fp->outbuffer,0,sizeof(fp->outbuffer));
return fp; // 至此上层拿到了这个被打开的文件
}
int Myfwrite(const char *message,int size,int num,My_FILE *fp) // 一定程度上减少系统调用次数,因为不一定所有的刷新条件都满足 --> 这个Myfwrite就是之前写的fwrite,是在进行把数据刷新到缓冲区里
{
if(message == NULL || fp == NULL)
return -1;
// 向C语言文件里面写,本质是向(不是操作系统,而是)缓冲区写
// fwrite这样的接口在我们看来90%的情况不是IO函数,而是一种拷贝函数,就好比用系统调用,比如write,
// 用write的时候把用户数据拷贝到内核当中(也只是拷贝),包括此时把内核中的数据(内存中)搬到了磁盘上,
// 不也相当于一种拷贝嘛!只不过把存储到外设这个拷贝起名叫IO
// 在计算机里面充满了大量的IO,包括未来学习网络部分的时候,把数据写到网络里面,从网络里读,
// 其实本质上依然是从设备当中拷贝数据到内存,内存当中拷贝数据到外设,所以一切皆拷贝,
// 当它都是拷贝时每个阶段不同的拷贝起个名字就叫IO
// 所以向C语言文件里面写,本质是向缓冲区写,至于怎么办就不用我们管了
int total = size * num;
//if(total + fp->size > fp->cap) // 没有考虑\0的问题
if(total + fp->size > fp->cap - 1) // 保证不会越界 --> 总容量给\0留个位置
return -1;
// 写入 --> 就相当于在进行数据拷贝 --> 核心
memcpy(fp->outbuffer + fp->size,message,total); // 没有考虑\0的问题,保险起见就加个1,这里已经考虑到了
fp->size += total;
fp->outbuffer[fp->size] = 0;
if(fp->outbuffer[fp->size - 1] == '\n' && (fp->mode & LINE_CACHE))
Myfflush(fp); // 如果字符串结尾是\n,并且模式是LINE(行缓冲),就直接强制刷新
//// fwrite判断是否刷新,这不就是刷新吗(所谓的刷新就是把数据拷贝给内存)!!!
//if(fp->size > 0 && fp->outbuffer[fp->size - 1] == '\n' && (fp->mode & LINE_CACHE)) // 刷新条件满足时才会调用系统调用
//{
// // 系统调用
// write(fp->fd,fp->outbuffer,fp->size);
// fp->size = 0; // 清空
// //fp->outbuffer = 0; // 不清空也行
// // printf格式化也是输出到缓冲区
//}
return num; // num: 一共写了多少字节
}
void Myfflush(My_FILE *fp)
{
if(!fp)
return;
// fwrite判断是否刷新,这不就是刷新吗(所谓的刷新就是把数据拷贝给内存)!!!
if(fp->size > 0) // 强制刷新,只要有数据就刷新
{
// 系统调用
// 所谓的刷新就是把数据从用户缓冲区拷贝到内核
// 像从用户缓冲区拷贝到内核这种模式叫做WB模式
// WB: Write Back(写回)
write(fp->fd,fp->outbuffer,fp->size);
fp->size = 0; // 清空
//fp->outbuffer = 0; // 不清空也行
// printf格式化也是输出到缓冲区
// WT模式,Write Though
// 不仅仅要写入到内核缓冲区,还必须写到对应的硬件上
fsync(fp->fd); // 强度做大一点
}
}
void Myfclose(My_FILE *fp) // 关闭文件
{
if(!fp)
return;
Myfflush(fp); // 只要有数据,无脑刷新
close(fp->fd); // 先刷新再关闭!先刷新,再关闭文件描述符!
}
结尾
uu们,本文的内容到这里就全部结束了,艾莉丝在这里再次感谢您的阅读!
结语:希望对学习Linux相关内容的uu有所帮助,不要忘记给博主“一键四连”哦!
往期回顾:
🗡博主在这里放了一只小狗,大家看完了摸摸小狗放松一下吧!🗡 ૮₍ ˶ ˊ ᴥ ˋ˶₎ა
更多推荐



所有评论(0)