winserver2008上用iis发布网站,嵊州网站制作,网站seo优化发布高质量外链,48快装旧房翻新公司电话系列文章目录 文章目录 系列文章目录前言浅谈文件的共识 一、 回忆c语言对文件操作的接口1.fopen接口和cwd路径2.fwrite接口和w#xff0c;a方法3.fprintf接口和三个默认打开的输入输出流#xff08;文件#xff09; 二、过渡到系统#xff0c;认识…系列文章目录 文章目录 系列文章目录前言浅谈文件的共识 一、 回忆c语言对文件操作的接口1.fopen接口和cwd路径2.fwrite接口和wa方法3.fprintf接口和三个默认打开的输入输出流文件 二、过渡到系统认识文件调用2.1看一看文件的系统调用接口——open2.2 write系统接口 访问文件的本质总结 前言
浅谈文件的共识 1.文件 内容 属性 2.文件分为打开的文件和未打开的文件 1打开的文件进程打开的。本质上是研究进程和文件的关系。 文件被打开就必须先加载到内存中。 一个进程可以打开多个文件操作系统要对这些文件进行管理就要先描述再组织。在内核中操作系统要管理好这些文件就必须有这个文件的对象包含很多的文件属性。 2未打开的文件有很多操作系统要将这些文件存储好本质上就是对这些文件进行增删查改的操作 未打开的文件在磁盘上放着。 本文章目标针对被打开的文件进行各种深入剖析。
一、 回忆c语言对文件操作的接口
1.fopen接口和cwd路径
先执行一下代码 1 #includestdio.h2 #includeunistd.h3 4 int main()5 {6 FILE* fp fopen(log.txt,w);7 if(fp NULL)8 {9 perror(fopen);10 return 1;11 }12 printf(pid: %d\n,getpid());13 fclose(fp);14 sleep(1000); 15 return 0;16 }
该程序运行起来后以w的方式打开log.txt文件如果该文件不存在则会创建一个文件。
运行起来时可以看到该进程的pid已经被打印出来。 ll查看能看到的确存在一个log.txt的文件。 那为什么是在当前目录下创建log.txt文件呢 这是因为一个叫做cwd的东西的存在。
在根目录下的proc目录下有该进程的当前路径。 即通过ls /proc/进程pid -l 可以看到该运行中的进程的cwd路径
并且该cwd路径就是可执行程序所在的路径
cwdcurrent work directory——当前工作目录
所以fopen以写的方式打开文件如果文件不存在就会在该进程的cwd路径下创建一个log.txt的文件
由此可以得出如果我们自己把该进程的cwd路径改了那么它就会在更改后的cwd路径下创建log.txt文件!
怎么改?
用一个接口chdir()即可更改当前的cwd路径。
chdir(/home/dzt/learning); 在上面代码的基础上在main函数开头就增加这一句代码后。
运行起来通过查找cwd路径发现cwd被修改了
且在/home/dzt/learning路径下发现 真就被创建了一个log.txt文件
且在进程对应的工作目录中不再有log.txt文件。 注意1.chdir也受权限的约束作为普通用户不能将路径修改到非/home/dzt路径下 2.如果fopen打开的文件带绝对路径那就按绝对路径来如果是相对路径就按该进程的cwd来! 总结这个小节讲了复习了fopen函数并且引入了cwd当前工作目录这个概念
2.fwrite接口和wa方法 fwrite的使用方法是将ptr这个字符串以size大小nmemb个长度写入stream文件指针指向的文件中。 w方法的特点是如果该文件不存在会创建一个文件。如果该文件存在会先将该文件清空再打开
注意这里的一个细节
6 const char* message Hello Linux\n;
17 fwrite(message,strlen(message),1,fp);执行该函数fwrite时是否需要strlen(message)1
答案是不需要的1是为了将字符串后面的’\0’也写入文件中可是
字符串以’\0’结尾是c语言的规定关文件操作什么事
所以并不需要1。
–
而a方法的作用是直接在文件的末尾追加字符串。
由此可知Linux中的 “” 和 两个符号的区别一定是一个以w方式打开一个以a’方式打开的区别 3.fprintf接口和三个默认打开的输入输出流文件
fprintf接口比我们常见的printf函数多了一个字符f默认情况下printf就是向显示器打印数据。
而Linux下一切皆文件所以显示器也是一个文件。
而fprintf接口就是向指定的文件中输入数据。
fprintf(stdout,%s %d\n,message,123); 而我们在运行该程序时会发现显示器中出现了这些信息这就是被打印到了显示器文件中而不是打印到其他文件中。 而这三个标准输入输出流就是对应的 键盘文件——stdin 显示器文件——stdout 显示器文件——stderr 一旦c程序运行起来就会默认打开这三个文件。
二、过渡到系统认识文件调用
文件其实是在磁盘上的磁盘是外部设备访问磁盘文件的本质其实是访问硬件
2.1看一看文件的系统调用接口——open 使用man 2 手册进行查找open接口的功能 man 2 open 该函数的功能是打开/创建一个文件或设备。 这里多嘴一句c语言中的fopen函数实现也是将这个open函数进行封装得来的。 pathname是文件路径如果传的是相对路径就是按进程所在的cwd路径为主。 flags是一个标志位 这里的标志位有三个 O_RDONLY表示只读操作 O_WRONLY表示只写操作 O_RDWR表示可读可写 为了更好地进行后面的传参下面来讲一个比特位传参的方式
看下面的代码 1 #includestdio.h2 3 #define ONE (10)4 #define TWO (11)5 #define THREE (12)6 #define FOUR (13)7 8 void show(int flags)9 {10 if(flagsONE) printf(hello function 1\n);11 if(flagsTWO) printf(hello function 2\n);12 if(flagsTHREE) printf(hello function 3\n);13 if(flagsFOUR) printf(hello function 4\n);14 15 }16 17 int main()18 {19 show(ONE);20 printf(\n);21 show(TWO);22 printf(\n);23 show(ONE|THREE);24 printf(\n);25 show(ONE|TWO|THREE|FOUR); 26 printf(\n);27 28 return 0;29 } 上图所示的代码定义了几个宏分别表示(1n位 传参时如果穿过来的flag是ONE则会打印function1 如果传的是ONE|TWO|THREE则传过去的flag的二进制为111 此时就能够匹配三个if语句就会打印出三个function。 通过这个例子就可以理解了open函数中的flags作为一个标志位未来会传很多比特位为1的宏如果传多个就能达到不一样的效果
下面看这个例子
1 #includestdio.h2 #includeunistd.h3 #includestring.h4 #includesys/types.h5 #includesys/stat.h6 #includefcntl.h7 8 int main()9 {10 // pathname, flags, modes11 int fd open(log.txt,O_WRONLY); //采用八进制默认权限位66612 13 if(fd 0)14 { 15 printf(open file error\n);16 return 1;17 }18 19 return 0;20 }
接下来的操作上打开一个文件因为传的是相对路径如果按照c语言的fopen函数如果该文件不存在那它就会在该进程的cwd路径下创建log.txt文件。
运行后会发现居然打开失败了 因为系统的open函数的O_WRONLY是只读的并没有创建文件的功能 要想解决这个问题只需要
int fd open(log.txt,O_WRONLY|O_CREAT);增加一个比特位传参即可 此时就创建出了一个log.txt文件
注意为什么log.txt的权限那么奇怪呢还是一些随机的权限
因为open函数中第三个函数 mode是权限我们没有传就默认是随机的
int fd open(log.txt,O_WRONLY|O_CREAT, 0666); //采用八进制默认权限位666 再传参之后重新试试结果如下 此时就相对正确了可是666权限对应的权限位应该是-rw-rw-rw- 明显不同这是因为权限掩码的存在默认的umask是2根据权限掩码和权限的计算规则
最终权限 起始权限 ~umask最终权限就是664–-rw-rw-r--
如果非要将权限设置成666就更改权限掩码
umask(0); 在全局中有一个umask(2)在该进程中也有一个umask(0)所以该文件创建之后其实是听进程中的umask(0)的因为**就近原则局部优先**进程的umask会影响整个进程但不会影响全局的。
此时log.txt的权限就非常正确了 总结这个小节讲了open函数的三个参数 pathname , flags , mode 2.2 write系统接口 write接口是向文件描述符对应的文件中写入。
文件描述符file descrpitor(fd)也就是open函数的返回值这个文件描述符就是一个文件的标识。 8 int main()9 {10 umask(0);11 // pathname, flags, modes12 int fd open(log.txt,O_WRONLY|O_CREAT, 0666); //采用八进制默认权限位66613 14 if(fd 0)15 {16 printf(open file error\n);17 return 1;18 } 19 20 const char* mesg Hello Linux;21 ssize_t id write(fd,mesg,strlen(mesg));22 23 return 0;24 }
此时向fd文件描述符对应的文件中写入Hello Linux; 结果显而易见就不展示了但是当我们将字符串修改成aaa时结果如下
20 const char* mesg Hello Linux;这个结果跟fwrite函数结果完全不同fwrite函数是每次打开文件都会清空内容再写入。
所以只需要小小地操作
12 int fd open(log.txt,O_WRONLY|O_CREAT|O_TRUNC, 0666); //采用八进制默认权限位666O_TRUNC就是truncate的简写。
通过O_WRONLY|O_CREAT|O_TRUNC选项就实现了如果文件不存在就创建如果文件存在就打开并先清空的逻辑
所以O_APPEND就是追加的逻辑
访问文件的本质
由此可知c语言cjava等任何其他语言对文件的操作接口的底层一定是对这些open函数write函数的封装 可是还有一个问题open系统调用的返回值是int fd而fopen函数的返回值是FILE* fp
这两者有什么关系
每次创建一个进程时都会在内存中创建一个描述该进程的task_struct对象包含进程中的各种信息其中就有一个叫做struct file_struct* files的指针该指针指向一个struct files struct数组且该数组中的所有成员类型都是struct file*的指针。
为什么要这样设计呢?
来看右边
每次打开一个文件时都会创建一个描述该文件的struct file文件对象该对象存储文件的各种信息。而该文件对象的地址就恰好被进程中的一个指针数组存储着 所以为什么open函数的返回值为int fd这个文件描述符其实就是进程中维护的指针数组存储该文件对象的下标
如果该文件对象的被存储在指针数组的2号下标处打开文件成功后就返回2fd
当我们尝试着打印该文件的fd时发现结果是3 这恰好证明了 该文件的文件描述符一定是放在进程管理的文件对象指针数组的3号下标处
可是为什么是3呢
因为前面说过一个进程创建后会默认打开三个输入输出流文件 这三个输入输出流分别是 stdin stdout stderr 分别对应的下标是 0 1 2 10 int main()11 {12 char buffer[1024];13 ssize_t sz read(0,buffer,sizeof(buffer));14 //sz返回读取到的个数15 if(sz 0)16 {17 perror(read fail);18 return 1;19 }20 buffer[sz] \0; // read是按字节读取如果想把它识别成字符串就得主动加\0 21 printf(%s\n,buffer);22 }
如上就是从0号文件中读取数据放入到buffer数组中。 运行起来后会发现结果就是等待输入等待键盘文件的输入。 read系统接口的注意事项返回值是返回成功读取到的字符的个数。如果想将读取到的若干字符识别成字符串需要主动添加’\0’。 下面再看一组测试代码 8 int main(){9 close(1);10 12 const char* msg Hello Linux\n;13 write(1,msg,strlen(msg));14 write(2,msg,strlen(msg)); 15 16 } 首先close 1号文件后运行结果只打印了一行msg代码。
前面说过1号文件是stdout对应的是显示器文件2号文件是stderr对应的也是显示器文件。它们本质上没有区别那为什么关闭了1号文件也就是关闭了显示器文件后通过2号文件仍然能向显示器中打印呢 1号文件和2号文件虽然都是显示器文件但是他们对应的struct file*指针不同也就是说有两个指针指向显示器文件。 关闭1号文件的本质是让1号文件对应的指针置空同时让显示器文件对应的引用计数减减。这个就是close函数的本质操作。 综上 C语言中将fd文件描述符封装成了FILE的结构体不止是c语言在任何其他语言中只要是文件操作的结构体就一定封装了fd文件描述符 总结
这篇文章讲述了关于文件的基础理解。 针对的是被打开的文件。