大丰市市城乡建设局网站,做一个招聘信息的网站_用什么做网站的软件,免费企业网站模板 php,房地产项目网站常识:
1 文件包括属性和内容
2 文件有打开和未打开文件#xff0c;
3 本文先讨论谁打开的文件#xff0c;以及如何管理已经打开的文件
一 回忆c接口
1 fopen 我们在test.c里面用一下fopen函数#xff0c;不存在打开的文件会默认创建#xff0c;那为什么默认新建在当前…常识:
1 文件包括属性和内容
2 文件有打开和未打开文件
3 本文先讨论谁打开的文件以及如何管理已经打开的文件
一 回忆c接口
1 fopen 我们在test.c里面用一下fopen函数不存在打开的文件会默认创建那为什么默认新建在当前目录下是因为cwd而不是PWD我们知道PWD是环境变量如果我们去到其它工作目录PWD会变但是CWD不变此时再运行一下test.c此时文件还是会创建在CWD存的路径下而不随着环境变量改变而变化。
2 fwrite fwrite函数可以往文件写数据值得一提的是fwrite的size参数这个是要写入的字节数我们大部分时候都是往文件写个字符串例如hello linux有时候size传strlen(hello linux)有时候传sizeof(hello linux)我们会发现sizeof多写入的\0被文件识别为乱码这说明一个问题字符串结尾有\0这个字符是c语言的规定文件是不认这个\0是字符的。 二 文件操作和系统调用 fwrite库函数一定封装了系统调用因为文件是在磁盘上的fwirte要往硬件写数据那不就相当于访问硬件的资源由于操作系统不相信用户所以fwrite一定不是直接把数据弄给硬件而是通过操作系统的接口将数据传给硬件。接下里就来认识认识几个系统调用。 1 认识open 从fopen的名字上看我们也知道fopen封装的是open接下来就看看open的参数和返回值。man 2 open就可从手册调出open的信息。 显然函数1是函数2的的子集合我们只要说清楚了函数2函数1的使用也就明白了。
参数1 文件名不带路径应该是默认在当前路径下找。
参数2 flags是什么呢? 我们要给flags传的是上面图片中的宏这些宏都表示一个一个的整数接下来就介绍介绍这些宏的意义以及如何使用。O_RDONLY表示open以只读方式打开O_WRONLY表示open以写方式打开, 而O_RDWR则表示以读写方式打开这三个宏最好只出现一个至于其它的宏O_APPEND这个是表示向文件写时以追加的方式去写。
使用: | 按位或?这个使用又是啥意思呢? 举个例子O_RDONLY可能是用0001来表示O_WRONLY则是0010O_RDWR则是0100来表示同理得O_APPEND就要用1000来表示只用了一个比特位就能唯一表示一个宏这样的设计非常巧妙首先我们可能会传多个宏设计者没有用可模板参数来接收而是只用一个整型因为我们可以对传的参数进行按位或这样只要对按位或的结果一分析就知道你打开文件是要读还是写了所以flag的类型就只是个朴素的int可其实里面门道也不少。
参数3 权限初始化因为我们open发现打开文件不存在要创建文件此时文件的权限是要指定的不然会给一个初始值但这个初始值如下图。 2 认识write和read
write和read就简单多了。 write:往fd这个文件描述符对应的文件写入buf数组中的元素字节数为count。 read:从fd这个文件描述符对应的文件读取count个字节的数据写入buf数组中。 现在我们就能解释现在我们就可以解释为什么fwrite就传一个w可以实现清空写以及创建文件w为什么能实现追加写就是因为在底层封装了这些宏然后操作系统识别到了在调用系统调用的时候传了给flag传了不同的宏至于返回值会在下面访问文件的本质中提及。
三 访问文件的本质 open的返回值-文件描述符这个文件描述符怎么是int类型呢我fopen用的可是FILE*这两者有什么关系吗? 先来看看操作系统如何管理文件首先操作系统打开的文件有很多这些必然会被操作系统管理操作系统管理文件就像管理进程一样只要用一个file结构体描述文件即可根本就不需要管文件内容这样在系统内核处就又增加了一个数据结构将所有的file结构体管理起来。诶不对啊文件不是进程打开的吗那不是应该进程管理吗如果仅仅是被进程管理那如果进程出异常了被kill了这些文件不就丢失了?所以系统必须也要管理。如下图: 好吧既然上面是系统管理文件的方式那进程呢怎么管理呢? 所以会有一个files_struct(这个和FILE*可不相同)来管理这个结构体内部会有一个数组数组每元素就是一个文件指针而文件描述符就是数组下标所以说一个文件描述符一定对应一个文件当然多个文件描述符可以对应同一个文件(file结构体内部肯定是会有引用计数记录的)。也就是说底层进程是通过下标来找文件指针从而找到文件的所以FILE*内部一定封装了文件描述符不然系统调用找不到文件。 我们发现所有语言写的代码运行起来都要默认打开三个文件stdin,stdout,stderror因为系统就要这样做系统设计这就认为开机后天然需要键盘显示器文件所以就要打开而所有语言写的代码不管写了啥形成进程后就会把已经打开的文件填到files_struct内所以程序一运行该数组内就有了三个元素。 既然stderror和stdout都是指向显示器文件它们的区别是什么我想也就是其内部的封装的文件描述符不同当我们close(1)printf就用不了了但是perror还可以向显示器打印。 四 重定向 周边小知识:write写的时候如果不close一直写那就会一直往后写而不是覆盖open以追加方式写指的是第一次write写的时候从哪开始写。
1 文件描述符的分配规则 自数组开头遍历在数组中最先遇到的空格位置的下标就是被分配的文件描述符。先前已经说了系统会打开两个文件(说打开三个是方便理解)这两个文件是键盘显示器而显示器被打开了两次在files_struct内的数组就会有三个文件描述符其中0存的是键盘文件指针1,2存的都是显示器文件指针我前面说多个文件描述符可以对应同一个文件这就是实实在在的例子。
2 手动实现重定向 输出重定向就是printf本来是要写给显示器的但是由于close(1)然后又打开了myfile.txt后占用了一号位(这就验证了文件描述符的分配规则)。 1 #includestdio.h2 #includestdlib.h3 #includeunistd.h4 #includestring.h5 #include sys/types.h6 #include sys/stat.h7 #include fcntl.h8 int main()9 {10 printf(我的id:%d\n,getpid());11 close(1);
W 12 int fd open(myfile.txt, O_CREAT|O_WRONLY,0666);13 printf(我的id:%d\n,getpid()); 14 return 0;15 }~所以两句printf就只有一句输出因为第二句printf就变成往myfile.txt输出了。 这也侧面说明在操作系统看来往显示器写和普通的文件没有区别而printf内部用的stdout一定是封装了1号文件描述符这是编码定死的printf也不管这个标识符对应的文件变了没拿到就写就有了输出重定向这种乌龙。
3 系统调用dup2 虽然可以先close再打开文件实现重定向但这样的代码还是不如直接调用系统调用那么优雅直接上代码看看使用。结果一致。 问题1 oldfd和newfd谁覆盖谁显然从先前的例子来看是oldfd上的内容覆盖到newfd的内容本质是数组对应下标上元素也就是文件指针的拷贝。 读者可以尝试进行输入重定向
同理也就是对0号位置下标做手脚