dw做的网站怎样才有域名,有什么网站可以推广信息,提升关键词优化排名软件,wordpress仿卢松松#x1f440;樊梓慕#xff1a;个人主页 #x1f3a5;个人专栏#xff1a;《C语言》《数据结构》《蓝桥杯试题》《LeetCode刷题笔记》《实训项目》《C》《Linux》《算法》
#x1f31d;每一个不曾起舞的日子#xff0c;都是对生命的辜负 目录
前言
1.#xff08;打开…
樊梓慕个人主页 个人专栏《C语言》《数据结构》《蓝桥杯试题》《LeetCode刷题笔记》《实训项目》《C》《Linux》《算法》
每一个不曾起舞的日子都是对生命的辜负 目录
前言
1.打开的文件管理
2.重定向
2.1dup2系统调用
3.如何理解Linux下一切皆文件
4.C语言中的FILE结构体
4.1FILE中的文件描述符
4.2FILE中的缓冲区 前言
本篇文章博主将会讲解『 Linux系统是如何管理文件的』『 进程与文件之间是如何联系的』『 重定向』以及『 C语言中的FILE结构体』的相关内容。 欢迎大家收藏以便未来做题时可以快速找到思路巧妙的方法可以事半功倍。 GITEE相关代码fanfei_c的仓库 1.打开的文件管理 一定时间段内系统中存在多个进程每个进程可能打开多个文件那么操作系统是如何管理『 打开的』文件的呢
我们之前学习进程时了解到操作系统对进程的管理是『 先描述再组织』核心是PCB-task_struct那么对于文件来说肯定也会存在这样一个结构体用来描述文件所以进程与文件之间的联系就变成了struct task_struct与struct XXX的联系。
这是我们的猜想让我们进入Linux内核一探究竟 果然如我们所料task_struct内部有一个结构体指针指向的就是文件结构体。
然后我们来分析files_struct 上篇文章我们提到在进程中文件描述符是标识不同文件的标识符每个文件都拥有自己的文件描述符文件描述符的分配规则为当前没有被使用的最小的下标作为新的文件描述符。
那我们来简要画一下文件管理的概念图
所以我们只需要知道文件描述符fd就可以通过该下标索引到对应的文件流上。 2.重定向
观察如果利用close关闭标准输出流printf的内容会被写入到哪
int main()
{close(1);//关闭标准输出流open(log.txt, O_WRONLY|O_CREAT|O_TRUNC, 0666);printf(hello linux\n);
} 所以我们得到文件描述符的分配规则
在files_struct数组中找到当前『 没有被使用』的最小下标作为新的文件描述符。
这样好像就完成了重定向的目的但是是不是有点太挫了有没有什么看起来更专业的方式来实现重定向呢
试想如果我们将文件描述符表中的内容『 直接作替换』是不是就完成了重定向的目的呢
2.1dup2系统调用 将oldfd索引内容拷贝给newfd索引内容。
int main()
{int fd open(log.txt, O_WRONLY|O_CREAT|O_TRUNC, 0666);dup2(fd, 1);printf(hello linux\n);
} 也实现了重定向的功能。 3.如何理解Linux下一切皆文件 既然说Linux下一切皆文件那么硬件设备也都是文件。
但是硬件设备的操作方法一定是不一样的。 那文件会操作不同的硬件如何屏蔽硬件差异呢 系统的设计一定是要通用的不然每新出一个硬件系统底层的文件结构体都要重新设计么 所以这里利用的是『 函数指针』的方式来访问不同的硬件操作方法无需关心底层是什么硬件设备。 4.C语言中的FILE结构体
这部分我们主要研究一下语言层面上是如何对文件管理进行设计的。
之前我们说 C标准库中的文件IO接口一定『 封装了系统调用』所以才能利用fopen()、fputs()等函数对文件进行操作。
4.1FILE中的文件描述符
之前我们学习C语言时fopen函数的返回值就是FILE的指针fp那现在我们学习了系统层面上的文件管理了解到『 文件描述符fd』才是唯一标识不同文件的属性值所以C语言中的FILE结构体中一定也封装了文件描述符fd。
我们来看一看C的源码
typedef struct _IO_FILE FILE;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
};
所以C语言中FILE结构体中对应的文件描述符叫做_fileno。
所以fopen是如何工作的呢
fopen函数在上层为用户申请FILE结构体变量并返回该结构体的地址(FILE*)在底层通过系统调用接口『 open』打开对应的文件得到『 文件描述符fd』并把fd填充到『 FILE结构体』当中的『 _fileno』变量中至此便完成了文件的打开操作。C语言当中的其他文件操作函数比如fread、fwrite、fputs、fgets等都是根据我们传入的文件指针找到对应的FILE结构体然后在FILE结构体当中找到文件描述符最后通过文件描述符对文件进行的一系列操作。 4.2FILE中的缓冲区
来段代码研究一下
#include stdio.h
#include unistd.h
#include string.hint main()
{// 使用system callconst char *s1 hello write\n;write(1, s1, strlen(s1));// 使用C语言接口const char *s2 hello fprintf\n;fprintf(stdout, %s, s2);const char *s3 hello fwrite\n;fwrite(s3, strlen(s3), 1, stdout);fork();return 0;
}
当我们直接运行时 可以看到write、fprintf、fwrite函数都成功将对应内容输出到了显示器上。
可当我们将程序的结果重定向到log.txt文件当中后 输出的结果就不一样了C语言函数fprintf和fwrite执行了两次系统调用write执行了一次为什么呢
这就与『 语言层面』上的『 缓冲区』有关了。
首先缓冲策略有以下几种
无缓冲。行缓冲。常见的对显示器进行刷新数据——遇到\n刷新全缓冲。常见的对磁盘文件写入数据——写满缓冲区才刷新
当我们直接运行程序时由于都是对显示器进行输出数据所以属于行缓冲行缓冲遇到\n数据就都刷新出来了也就是说此时缓冲区中无内容创建子进程不会发生修改不会发生写时拷贝所以子进程结束也不会有新的内容被刷新出来。
而重定向到文件中属于全缓冲全缓冲缓冲区写满才刷新所以对于C语言函数fprintf和fwrite来说数据被写入到了C语言自带的缓冲区当中之后当我们使用fork函数创建子进程时由于进程间具有独立性之后当父进程或是子进程对要刷新缓冲区内容时本质就是对父子进程共享的数据进行了修改此时就需要对数据进行写时拷贝也就是说父子各一份缓冲区当父子进程结束时都刷新缓冲区所以重定向到log.txt文件当中printf和puts函数打印的数据就有两份。但由于write函数是系统调用接口系统调用接口没有语言层面上的缓冲区系统中当然也有缓冲区但不受我们控制我们可以将write函数看作是没有缓冲区的因此write函数打印的数据就只打印了一份。 注意我们这里研究的缓冲区都是语言层面上用户级缓冲区 操作系统也当然会提供相关内核级缓冲区不过不在我们探讨的范围之内。 一个文件一个缓冲区缓冲区在文件结构体FILE内部做管理。
typedef struct _IO_FILE FILE;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
}; tips你知道格式化输入、格式化输出在哪里发生么
比如
printf(%d,12345);
实际上就是在用户级的缓冲区上发生的比如向显示器打印一个整型数据12345实际上显示器显示的是字符1字符2字符3字符4字符5所以整形数据在送到内核级缓冲区之前首先要在用户级缓冲区上被转化为字符。 如果你对该系列文章有兴趣的话欢迎持续关注博主动态博主会持续输出优质内容
博主很需要大家的支持你的支持是我创作的不竭动力
~ 点赞收藏关注 ~