iis架设网站教程,网络有限公司名字大全,深圳企业管理培训查询,wordpress 在线预览目录
一、文件基础认识
二、C语言操作文件的接口
1. 和
2.理解“当前路径”
三、相关系统调用
1.open
2.文件描述符
3.一切皆文件
4.再次理解重定向 一、文件基础认识
文件 内容 属性。换句话说#xff0c;如果在电脑上新建了一个空白文档#xff0…目录
一、文件基础认识
二、C语言操作文件的接口
1. 和
2.理解“当前路径”
三、相关系统调用
1.open
2.文件描述符
3.一切皆文件
4.再次理解重定向 一、文件基础认识
文件 内容 属性。换句话说如果在电脑上新建了一个空白文档它虽然没有内容但也是占据磁盘空间的。想要修改一个文件的内容比如用WPS这样的软件操作文件内容本质上都需要CPU完成相关的指令而CPU又只与内存交互所以打开文件的含义其实就是把文件加载到内存中。在我们眼里我们双击了一个文件就是打开了文件但是在操作系统看来并不是我们打开了文件而是某一个正在运行的进程文件是由进程打开的。一个进程可以打开多个文件。操作系统管理多个被打开文件必然也会像操作系统管理多个进程一样利用面向对象和数据结构因此内核中必然定义了结构体来描述被打开的文件。从操作系统管理文件的角度看文件被区分为被打开的文件在内存中和没有打开的文件在磁盘中。
二、C语言操作文件的接口 fopen以w方法打开一个文件。
#include stdio.h
#include stdlib.h
int main()
{FILE* pf fopen(aaa.txt,w);if(pf NULL){perror(fopen:);return 1;}const char* str aaaaaaaaaaaaaaaaaaaaaa\n;fputs(str,pf);fclose(pf);return 0;
}utocooutocoo-virtual-machine:~/Desktop/linux/241121$ ./a.out
utocooutocoo-virtual-machine:~/Desktop/linux/241121$ cat aaa.txt
aaaaaaaaaaaaaaaaaaaaaa
utocooutocoo-virtual-machine:~/Desktop/linux/241121$ 结果显示文件aaa.txt中已经写入了一段字符串。修改源代码将写入字符串的代码删除后再执行编译运行一次。
#include stdio.h
#include stdlib.h
int main()
{FILE* pf fopen(aaa.txt,w);if(pf NULL){perror(fopen:);return 1;}
// const char* str aaaaaaaaaaaaaaaaaaaaaa\n;
// fputs(str,pf);fclose(pf);return 0;
}utocooutocoo-virtual-machine:~/Desktop/linux/241121$ cat aaa.txt
aaaaaaaaaaaaaaaaaaaaaa
utocooutocoo-virtual-machine:~/Desktop/linux/241121$ gcc file.c
utocooutocoo-virtual-machine:~/Desktop/linux/241121$ ./a.out
utocooutocoo-virtual-machine:~/Desktop/linux/241121$ cat aaa.txt
utocooutocoo-virtual-machine:~/Desktop/linux/241121$ 结果表明aaa.txt文件中的内容都消失了。原因在于fopen打开文件的方式w使用man手册查看fopen打开文件方式的说明。 w方式打开文件时会先清空文件中的所有内容。如果想保留文件中原来的内容做写入操作就应该使用a的方式打开文件。 1. 和
utocooutocoo-virtual-machine:~/Desktop/linux/241121$ cat aaa.txt
utocooutocoo-virtual-machine:~/Desktop/linux/241121$ echo aaaaaaaaaaaa aaa.txt
utocooutocoo-virtual-machine:~/Desktop/linux/241121$ cat aaa.txt
aaaaaaaaaaaa
utocooutocoo-virtual-machine:~/Desktop/linux/241121$ echo bbbbbbbbb aaa.txt
utocooutocoo-virtual-machine:~/Desktop/linux/241121$ cat aaa.txt
bbbbbbbbb
utocooutocoo-virtual-machine:~/Desktop/linux/241121$ 通过echo做重定向操作向aaa.txt文件中先后写入两次最终效果并不是有两段字符串说明重定向操作符打开文件的方式本质上也是w的方式。需要一提的是echo重定向到文件中本质上也要修改文件的内容所以一定会打开文件。 utocooutocoo-virtual-machine:~/Desktop/linux/241121$ echo aaaaaaaaaaaaaaaa aaa.txt
utocooutocoo-virtual-machine:~/Desktop/linux/241121$ cat aaa.txt
aaaaaaaaaaaaaaaa
utocooutocoo-virtual-machine:~/Desktop/linux/241121$ echo bbbbbbbbbbbbbbbb aaa.txt
utocooutocoo-virtual-machine:~/Desktop/linux/241121$ cat aaa.txt
aaaaaaaaaaaaaaaa
bbbbbbbbbbbbbbbb
utocooutocoo-virtual-machine:~/Desktop/linux/241121$ 而追加重定向操作符 先后向aaa.txt文件写入两次后最终效果是两段字符串都被保留了下来说明 其实和a方式类似是一种追加的形式。 2.理解“当前路径” 在使用C接口操作文件的时候经常会听到说“如果没有这个文件则在当前路径下新建这个文件”如何理解这个“当前路径” 最简单直接的理解就是我们当前程序的路径。
//file.c
#include stdio.h
#include stdlib.h
int main()
{FILE* pf fopen(aaa.txt,w);if(pf NULL){perror(fopen:);return 1;}fclose(pf);return 0;
}当前路径就是file.c文件所在路径编译运行前该路径下没有aaa.txt文件编译运行后该路径下存在名为aaa.txt的文件。
utocooutocoo-virtual-machine:~/Desktop/linux/241122$ ll
总计 16
drwxrwxr-x 2 utocoo utocoo 4096 11月 22 12:22 ./
drwxrwxr-x 16 utocoo utocoo 4096 11月 22 12:19 ../
-rw-rw-r-- 1 utocoo utocoo 233 11月 22 12:19 file.c
-rw-rw-r-- 1 utocoo utocoo 64 11月 22 12:21 Makefile
utocooutocoo-virtual-machine:~/Desktop/linux/241122$ make
gcc -o file file.c
utocooutocoo-virtual-machine:~/Desktop/linux/241122$ ./file
utocooutocoo-virtual-machine:~/Desktop/linux/241122$ ll
总计 32
drwxrwxr-x 2 utocoo utocoo 4096 11月 22 12:23 ./
drwxrwxr-x 16 utocoo utocoo 4096 11月 22 12:19 ../
-rw-rw-r-- 1 utocoo utocoo 0 11月 22 12:23 aaa.txt
-rwxrwxr-x 1 utocoo utocoo 16048 11月 22 12:23 file*
-rw-rw-r-- 1 utocoo utocoo 233 11月 22 12:19 file.c
-rw-rw-r-- 1 utocoo utocoo 64 11月 22 12:21 Makefile在文件基础认识部分已经提到过文件是由进程打开的那么新建一个文件也是由进程完成进程是如何知道在哪条路径下新建一个文件呢。 在源代码中打印出进程的PID运行后再在/proc路径下找到对应进程的所在目录。
while(1)
{printf(PID:%d\n,getpid());sleep(2);
}PID:2930
PID:2930
PID:2930
PID:2930
PID:2930
PID:2930
PID:2930当前路径在进程的属性中其实已经保存好了是cwd这条信息。因此新建一个文件要被存放到哪里也是确定的。但是进程的工作路径是可以修改的虽然进程的前身是一个可执行程序可执行程序的路径是确定但是当可执行程序被操作系统管理起来后变成进程进程的工作路径是可以通过chdir指令修改的那么修改路径后再新建一个文件这个文件的所在路径不再是修改前的路径了而是修改后的路径。 这就表明所谓的当前路径其实是进程在运行的时候的工作路径这个路径是由进程自己记录的就是那条cwd信息。
三、相关系统调用 系统默认打开三个流stdinstdoutstderr这三个流对应的外设分别为键盘、显示器、显示器。而Linux管理外设是以文件的方式即必然存在系统调用system call。因此C语言的fopen、fclose、fwrite等函数本质是调用了system call。 下面就来认识Linux下文件相关的system call。
1.open pathname就是路径传参方法和C语言的fopen的参数差不多。flags类型为int传参的可选项如下所示 这些值都是C语言定义的宏目的是为了实现只定义一个函数却可以同时“传两个参数”。比如 #include stdio.h
#define ONE 1
#define TWO (11)
#define THREE (12)
#define FOUR (13)
#define FIVE (14)void Print(int flags)
{if(flags ONE)printf(1\n);if(flags TWO)printf(2\n);if(flags THREE)printf(3\n);if(flags FOUR)printf(4\n);if(flags FIVE)printf(5\n);
}
int main()
{Print(ONE);printf(-----------------\n);Print(TWO);printf(-----------------\n);Print(ONE|TWO);printf(-----------------\n);Print(ONE|FOUR|FIVE);return 0;
}如果使用两个形参的open接口一般是操作已经存在了的文件比如bbb.txt文件必须存在否则会报错。
int main()
{int fd open(bbb.txt,O_WRONLY);if(fd -1){perror(open\n);return 1;}close(fd);return 0;
}由于bbb.txt不存在则fd-1 用open接口实现fopen的w方式文件如果不存在则新建。而新建一个文件会有权限的初始化一般普通用户新建一个文件的权限是0666-rw-rw-rw-而普通用户的权限掩码umask为0002实际权限等于初始化权限减去权限掩码即-rw-rw-r-- mode即初始化权限码一般传0666只有flags带O_CREAT时mode传参才有效。 一般新建一个文件在open的第二个参数上应该传新建、可写、写入时清零等同于fopen的w方式。
int main()
{int fd open(bbb.txt,O_WRONLY|O_CREAT|O_TRUNC,0666);if(fd -1){perror(open\n);return 1;}const char* msg this is open to w\n;write(fd,msg,strlen(msg));close(fd);return 0;
}原来不存在的文件bbb.txt被创建了出来并且o的权限少了w符合预期。 2.文件描述符 再来理解open的返回值——文件描述符int fd——Linux用整型值描述被打开的文件。 这些整型值其实是数组下标我们知道系统默认打开三个流其实是三个文件stdin、stdout、stderr它们的下标对应为0、1、2如果先后有序的打开1.txt、2.txt、3.txt则它们的下标也是有序的为3、4、5。 这段话似乎让你很懵不过我马上就要阐述具体的内容。 在此之前要明确操作文件只能由操作系统来做因此有C语言的fopen封装open接口有C语言定义的FILE指针的流封装文件描述符fd。 实际上FILE类型是结构体类型也是封装了文件描述符int fd。 对int fd的理解。 文件描述符的本质就是数组下标。
OS管理进程这一板块叫做进程管理有PCBLinux下被定义为task_struct。OS管理文件这一板块叫做文件管理在之前介绍了文件区分为内存中的文件和磁盘中的文件被加载到内存中的文件OS要对它们做管理就必然做“面向对象”和“数据结构”的工作“面向对象”就是定义结构体“数据结构”就是把对象存储到链表或者其他数据结构里面。Linux下把这个结构体类型定义为file结构体内容大致有属性、方法集、缓冲区、mode权限码、flag、pos以及指向下一个结点的next等。进程管理和文件管理是两个独立的板块但是又有关联。进程可以打开多个文件那么一个进程打开了哪些文件该进程必然要做记录。于是Linux下task_struct结构体中有一个结构体指针指向的结构体类型为files_struct而这个结构体中有一个数组数组的每个元素类型为结构体指针指针指向的结构体类型为file这个数组被称为文件描述符表。 一个进程打开文件后进程在这个数组中保存指向这个文件的指针默认这个数组的前三个位置已经被stdin、stdout、stderr这三个文件占用了。 而数组下标就是文件描述符为什么close、write等这些接口都用int类型的文件描述符来操作文件原因很简单数组下标式访问仅仅是O(1)复杂度。
3.一切皆文件 硬件一层由于各种原因设备的操作方法各不相同因此每台计算机都需要装载相应的驱动。而对于每台设备的操作函数它们的函数类型相同函数内容各不相同。 file结构体定义了方法集本质就是函数指针。
每一台设备被视为一个结构体方法集指向了该设备的操作方法。当系统调用read读取某个外设的内容实际上就是函数回调的形式用函数指针调用外设的读函数。 4.再次理解重定向 文件描述符的分配规则一定会把最小的数组下标利用起来如果存在没有被利用的较小下标则会分配给最新打开的文件比如打开b文件前将已经打开的a文件关闭则打开b文件后a文件的较小fd会分配给b文件。 上面这段话其实就是重定向的实现原理。 输出重定向本该输出到屏幕的语句却输出到了bbb.txt。
int main()
{close(1);int fd open(bbb.txt,O_WRONLY);printf(这段话本该输出到屏幕\n);return 0;
}原因就是在执行完close(1)语句后当前进程的文件描述符表中数组下标为1的位置不再是指向屏幕文件的指针而又打开了bbb.txt文件则1号下标的指针指向了bbb.txt文件printf底层封装的write传参的fd值还是1因此这句字符串被写进了1位置指向的bbb.txt文件的缓冲区。 所以重定向的本质就是文件指针在文件描述符表中的位置发生了变化文件描述符表是一个数组即数组下标发生了改变比如原来4号下标指向A.txt文件通过重定向让1号下标指向了A.txt这样一来上层向显示器打印的内容其实都被输出到了A.txt。 有一个专门用来拷贝文件描述符的系统调用——dup 想把打印到屏幕的内容重定向到bbb.txt可以用dup2来实现。 大致意思是用oldfd的值覆盖到newfd。
int main()
{int fd open(bbb.txt,O_WRONLY);dup2(fd,1);printf(----\n);return 0;
}