外贸网站建设公司咨询,建网站成本,怎么介绍自己的学校,如何发布自己的html网站1.基础IO #xff08;1#xff09;文件操作 在C语言中#xff0c;用来进行文件操作的函数有很多#xff0c;比如#xff1a; 所谓文件操作#xff0c;简而言之就是通过语言层面向系统层面进行函数调用#xff0c;命令操作系统为在磁盘上为其创建文件#xff0c;那么这些…1.基础IO 1文件操作 在C语言中用来进行文件操作的函数有很多比如 所谓文件操作简而言之就是通过语言层面向系统层面进行函数调用命令操作系统为在磁盘上为其创建文件那么这些函数的使用如下 返回值为FILE*是一个结构体指针此结构体保存的就是刚刚进行完文件操作后针对文件属性的一个结构体指针。 而以fopen函数为例可以看到其中一个名为mode的参数参数是const char*也就是不可更改的字符串类型那么在这里代表了要对文件进行什么操作比如r为读readw为写write并且是覆盖式地写并不会保存原来的数据a为在保存原来数据的基础上进行追加append。 那么除了fopenfclose对文件进行写操作后还要考虑如何将其读出来所以针对文件读取也有很多函数比如 可以发现这些函数基本上都有FILE* stream这个参数那么上文提到过FILE*是一个关于存储文件属性的结构体而stream学过C的朋友不难理解stream就是“流”的意思也就是说向这些函数中传入FILE* 类型的流参数而这个参数所指向的就是刚刚的文件并且可以从这些文件中读取字符字符串等。 2stdin、stdout、stderr 了解过C语言的文件函数后我们又要提出一个疑问我们在进行上述对文件的写入操作后数据确实被写入了文件中然后通过fgets函数将字符串获取到了buffer中最后打印到屏幕上那么文件中的数据和屏幕上的数据有什么分别屏幕这个外设为例以及其他外设是否也和文件一样具有某种相同属性呢 在编写C语言程序时候会默认打开三个IO流stdin、stdout、stderr分别代表标准输入标准输出和标准错误C也有相同的流那么即cin、cout、cerr那么其中标准输入是通过键盘向“文件”中写入数据而标准输出和标准错误都是通过屏幕将数据打印出。 可以看到这三个标准流的类型都是我们刚刚看到过的FILE*通过刚刚的介绍相信不难理解这三个流肯定是和文件有关系的所以我们可以更加坚信刚刚的理论我们所使用的键盘、屏幕、鼠标等等外设都可以和文件放到一起考虑 所以可以得出结论操作系统中一切皆文件 那么我们回过头来看一开始的文件函数既然fputs是向文件中放数据那么如果屏幕是文件的话可以直接将fputs的第二个参数改为stdout吗 我们可以看到将fputs函数的第二函数也就是输出目标文件从刚刚的fp改为stdout和stderr数据成功地被打印到了屏幕上所以我们的猜测是正确的一切皆文件使用文件操作把目标从文件换为外设数据依然可以正常被打印。 stdout和stderr都是在屏幕上打印所以字符串都被打印出来了但是可以注意到使用重定向到log.txt时却只有stdout可以为什么因为重定向全名叫做输出重定向也就是只把stdout的内容重定向到文件中这里的输出重定向并不包括错误重定向。 所以我们调用的一切函数都会从语言层面跨越到操作系统层面最终都是访问硬件显示器、键盘、文件磁盘所以OS就是硬件的管理者。 3文件的系统调用接口 那么之于Linux操作系统和C语言不同的是它也有自己独有的系统调用接口使用这些接口可以进行系统编程直接对系统进行操作。 与C语言中 fopen对标的就是open函数同为打开文件的函数并且由于Linux底层就是C语言完成的所以在参数方面可以发现和fopen差不多第一个也是文件的路径第二个是对文件的操作第三个是文件的权限返回值成功0的数0则表示失败。 如果open不写mode_t的参数的话系统默认为新创建的文件设置的权限是乱的。 那么其中的O_WRONLY | O_CREATE又是什么意思呢wronly代表的就是write only也就是只写后面的create代表如果这个文件在打开时没有被创建则创建之而这两个关键字在底层可以理解为定义好的宏将这些关键字的二进制分别设置不同的位置并且对它们进行按位或然后在OS内部再将传入的flags与每个标志位进行按位与就能得到它们哪个位置设置了1就能知道要如何操作该文件了。 那么针对返回值我们可以发现系统调用的接口返回值都是int大于0肯定是成功那么这个返回值的作用难道只是简单的代表文件创建成功与否吗并不是在Linux中新创建或者打开的文件都会有一个专门的fd标识了其唯一的身份而open、close等函数的返回值就是通过该fd打开、关闭某个文件。 那么我们不妨做个实验只是随机打开一个文件看一下fd会是多少 我们知道Linux的底层使用C语言写的而一看到数字我们就会非常敏感的将其和数据结合起来上文中提到了每个C程序都会默认打开三个标准流那么这三个标准流就是隐藏起来的0 1 2所以我们也就知道为什么新创建的文件下标会从3开始了。 文件描述符fd的分配规则即未使用过的从小到大的使用。 所以如果我们继续创建几个文件呢fd会如何变化 那么我们知道所有的文件操作其实本质的上都是一个进程对某个文件执行响应的操作要想操作文件首要的就是先打开文件了解电脑简单运行原理的朋友肯定清楚当一个进程被打开时首先就需要将其数据从磁盘拿到内存cpu再从内存中对其进行处理那么其中的“数据”是什么呢我们刚刚提过操作系统中一切皆文件也就是说系统层面无论硬件软件其属性底层都有着相似的结构所以这里的数据也就可以理解成该文件的相关属性。那么就又有一个疑问了我们平时如果创建了一个空文件即使里面没有数据那么该文件就一定是空的吗并不是因为操作系统在创建该文件时会给它在磁盘中申请空间储存其相关的文件属性这个属性就包括了文件创建的时间文件的权限文件的描述符等等。 所以我们可以理解系统运行程序本质就是对文件的操作——将文件打开并将其数据和属性加载到内存那么既然如此的话操作系统中是不是就有着大量的文件呢是的操作系统和文件的关系就是1:n的关系操作系统打开大量的文件并不是稀奇事那么如何管理就是一大关键问题了那么如何管理呢先描述再组织。 先描述也就是先将待管理的文件针对其属性创建响应的数据结构struct file此结构体中就包括了打开的文件的相关属性和链接属性描述完毕再按照此结构体进行管理。 当然Linux针对系统函数的读写操作也有相对应的函数分别名为write和read其使用方法和C语言的接口非常类似无非就是从哪里读都多少读到哪里向哪里写写什么内容写多少数据。
2.一切皆文件 刚刚我们首次提到了一切皆文件的概念即我们所熟悉的外设磁盘本质上都和类似.txt文本文件有着相似的结构其操作在操作系统内核看来一般无二 那么虽然一切皆文件这个概念很大一统但是我们必须知道比如键盘、显示器、磁盘、显卡等外设它们即使是文件但是和我们常见的文本文件还是有着本质的区别的。 很类似C中的多态我们知道在多态中子类在继承父类后重写了父类的方法那么在使用父类的指针调用这两个方法时传入的是子类完成的就是子类的操作反之则是父类的。
3.重定向 我们刚刚提到了输出重定向即向文件输入的数据变为向屏幕上打印在有了一切皆文件这个概念后我们就不难理解了屏幕和我们创建的log.txt本质上都是文件都有其相对应的文件描述符。 那么重定向就只有输出重定向吗既然重定向是将本该写入某个文件的数据该为写入另一个文件那么可以不写入屏幕吗换言之可以将输出重定向改为文件和文件之间的重定向吗 我们知道stdout代表了标准输出其文件描述符默认设置为1如果我们将这个文件描述符使用系统调用接口close关闭掉同样是文件结果是肯定是会成功的那么关闭后使用open函数打开一个文件我们知道系统会为该文件分配新的文件描述符而分配的规则就是从小到大找未被使用过的也就是刚刚关闭的1之后再执行printf函数该数据将会向哪里打印呢 我们可以发现本该打印到屏幕的数据被打印进了log.txt文件而log.txt的文件描述符我们刚刚关闭的1所以这更加加深了我们对一切皆文件的理解即使操作名为输出重定向但是其本质就是文件和文件之间的重定向因为一切皆文件。 除了输出重定向在Linux中还有其他的重定向方式本质都是文件和文件之间对于字符串的操作。 那么在Linux的系统接口中针对重定向也有专门的函数 通过介绍不难发现此接口的功能就是针对老的文件创建一份其拷贝作为新文件并且以后的数据将重定向到此第二个参数依旧是对于该文件的操作。 那么我们可以针对上述的操作使用系统接口dup来实现输出重定向 使用write函数也可以完成相同的功能 但是此时我们发现当查看重定向后的log.txt时只有标准输出打印出来了为什么标准错误没有打印呢标准输出和标准错误难道不是同样是向屏幕打印吗为什么一起重定向后产生了不同的结果呢其本质还是只是输出重定向并不会把标准错误的信息重定向到文件中那么如果我们想实现此操作只需加一条指令 此操作就是将标准输出和标准错误两个文件同时向一个log.txt文件中进行重定向在逻辑层面可以理解为此指令将stdout、stderr之前的执行流进行了更改同时更改进了log.txt。
4.缓冲区 缓冲区——在我们的理解层面是一块区域作用是做某些缓冲的为了不使某些操作太拥挤目的就是为了给我们想实现的某些操作更多的安全性。 那么之于操作系统也有其相应的缓冲区那么这个缓冲区究竟是干嘛的首先我们先看一段代码 我们知道在关闭了1号文件描述符后open函数打开的文件将会自动使用1号描述符从而模拟了重定向操作而1号描述符恰好就是stdout所以此操作也叫做输出重定向所以我们将此进程连续执行三次后由于文件的操作加上了追加操作所以三次操作后重定向后的文件log.txt里面有三条消息这并不难理解那么再来看一段代码 当加上了close(fd)后为什么本该重定向进log.txt的数据没有了呢为什么去掉close(fd)后就有呢为了解决此疑问正式引出缓冲区的概念 当我们调用了C语言接口后比如一条简单的printf函数在用户层使用了此函数那么接下来这个函数会向下调用通过系统调用接口调用操作系统的接口将待打印的数据从C语言缓冲区写入系统缓冲区因为一切硬件都是由操作系统管理所以屏幕也不例外所以最后由操作系统来调用系统接口向屏幕上打印数据。而在这个过程中是一定需要fd的也就是文件描述符。 而上述的重定向操作系统的标准输出流分别指向了不同的文件而不同文件针对数据有着不同的刷新策略从哪里刷新就是从缓冲区中采取哪种刷新策略代表了不同种类的文件。 所以log.txt中没有数据的原因是;先关闭了close(1)又创建了fd打开了log.txt进行了输出重定向现在stdot指向的是log.txt将数据全部写入log.txt后由于显示器是行缓冲文件是全缓冲而缓冲区又没满所以数据一直在log.txt中没有被刷新到显示器而这时如果关闭了fd就关闭了1相当于文件刷新到显示器的桥梁就断了所以数据一直在内核缓冲区中如果想看到数据可以在close(fd)前加上fflush(fd)此函数是刷新缓冲区数据会立即刷新到显示器上。 所以在语言层面会有专门的缓冲区在调用print函数时数据会先写入语言缓冲区并且调用语言的print函数时在底层系统会自动调用系统接口所以数据会从语言的缓冲区写入内核缓冲区最终打印到显示器。 所以这也是为什么每次在写完打印函数后都要加一个\n字符就是因为在语言层面无论是C还是C都有自己的缓冲区而它们的刷新策略都是行刷新这也就是为什么\n叫做换行符。
5.文件系统 我们之前讲到创建一个文件时无论是否向文件中写入了数据在磁盘上都会为这个文件开辟空间在底层都会有其对应的结构体对其进行管理所以我们可以理解为文件 文件属性 文件内容。 而文件名在系统层是没有意义的因为操作系统区分文件的方法并不是靠文件名而是通过inode编号一个文件一个inode。 在计算机中磁盘是计算机的硬件机械设备其作用就是用来保存各式各样的文件那么首先将磁盘分区再将文件系统写入也就是通常所说的格式化这样一来磁盘就被线性式地划分好了而每个分区就用来存储不同的文件。 每个磁盘都可以理解为分成了Block group 0~Block group n以一个Block group为例对文件来说最重要的就是Inode BitmapInode TableData Blocks。 Data Blocks用来存文件的内容用一个个小空间存。 Inode Table用来存储文件的属性也是类似于Data Blocks的划分只不过空间更小用来存一个个结构体。 Inode Bitmap用来存inode Table中哪些用过了哪些没用过这样存文件就能节省时间不用遍历。 因为Linux内核是由C语言编写所以这些属性在底层中用结构体来存储再合适不过了所以每个文件在底层操作系统都会为其分配结构体struct file其中存储对应的属性值。 以这样的方式管理好一块分区后再以同样的方式去管理其他分区由小到大管理好整个磁盘。 而目录也是文件在操作系统中一切皆文件所谓目录就是路径在底层可以理解为二叉树的形式目录中存放的就是文件名和inode编号的映射关系。 6.软硬链接 我们在Linux中使用 [ln -s 源文件名 目标文件名] 的方式可以将某个文件指定成另一个文件的软连接。 除了两个文件指定软链接外还可以指定一个文件为另一个文件夹下某个文件的软链接例如我们某个文件夹下有个可执行程序使用软链接就可以迅速找到它相当于windows中的快捷方式。 而软链接有自己独立的inode有inode就说明软链接在底层也是文件 也有自己的属性自己的数据块可能保存的就是链接所指向的文件所在路径和文件名。 而与之相对的硬链接和软链接就有本质的区别它不再是一个文件在底层中并未对其分配inode本质并不是一个独立的文件而是一个文件名和inode编号的映射关系因为没有独立的inode即也没有属性数据块等。 所以创建硬链接的本质是在特定的目录下填写一对文件名和inode的映射关系。 我们可以发现软链接对应的两个文件的inode是不同的因为是两个文件而硬链接是相同的说明硬链接在链接后还是相同的文件可以理解为起别名。 file.txt刚创建出来时只有一个硬链接代表file.txt和inode只有一个映射关系创建file_hard文件并且和file.txt产生硬链接后就有了两组映射关系其中struct inode中可能也有一个int ref用来记录指向文件的数目如果多来一个指向ref也就是引用计数。 而这里的. 和..代表上级目录和上上级目录为什么.的硬链接也是2呢我们上面说过目录也是文件也有其对应的inode所以它有着和文件相同的属性这里的硬链接除了它本身之外我们也可以通过它打开的文件找到它以它路径下被打开的文件的角度来看也有一条硬链接以bin目录和其目录下的log.txt为例bin的inode和log.txt中的上级目录的inode是相同的所以是两个硬链接数。