文件对于咱们来讲,貌似再普通不过了,windows 使用扩展名来区分不一样的文件,咱们接触了 gif、docx、pdf、mp三、mp四、exe 等等好多好多不一样的文件,可是它们在磁盘中都长一个样子,对于内核而言,没有什么区别。node
文件这个概念就是对 I/O设备 的抽象,一个文件就是一串 m 个字节的序列,全部的 I/O 设备(网络、磁盘、终端)都被模型化为文件,全部的输入输出都被当作对相应文件的读和写来执行。linux
稍微特殊一点的是文件目录吧,目录也是一个文件,它是包含一组连接
的文件,其中每一个连接都将一个文件名映射到一个文件,这个文件也多是另外一个目录。git
每一个目录至少会有两个条目,.
是到该目录自身的连接;..
是到父目录的连接,你可使用mkdir
命令建立一个目录,而后查看其内容观察观察,linux 用ls
查看,windows 用dir
。程序员
每一个进程都有一个当前工做目录,linux 是以/
做为根目录的,因此绝对路径就是一个/
开始,以下图所示,hello.c 的绝对路径名即为:/home/droh/hello.c
;若是进程的当前工做目录为:/home/droh
,那么 hello.c 的相对路径就为:./hello.c
,而要是工做目录为:/home/bryant
,那么相对路径名则为:../home/droh/hello.c
。github
咱们可使用open
函数来打开一个文件,它的做用是把文件名转换为一个文件描述符,这个描述符是一个小的非负整数,在后续对此文件的全部操做中标识这个文件。open
函数返回的描述符老是在进程中当前没有打开的最小描述符。shell
Linux shell 建立的每一个进程开始都有三个打开的文件:标准输入(描述符为 0)、标准输出(描述符为 1)、标准错误(描述符为 2)。编程
open
函数有三个入参,分别为filename, flags, mode
,flags 指明了如何访问这个文件,即只读、只写、可读写;mode 指明了新文件的访问权限位,它经过与进程自带的umask
进行运算来得到文件的访问权限,这个运算时:mode & ~umask
。windows
读文件是从描述符为 fd 的当前文件位置复制 n 个字节到内存位置 buf,写文件则是把内存中的字节复制到当前文件中,系统中提供了 read
和write
函数来提供相应的功能。网络
read
函数的返回值是表明实际传送的字节数,有趣的是当出错时,它须要返回 -1,所以使用的是一个有符号长整数,而就仅仅为了这个 -1,就使得read
的最大值减少了一半。数据结构
固然还有可能会遇到须要读取的字节比文件实际字节数要多的状况,这时就会触发一个称为 end-of-file(EOF) 的条件,应用程序死能够检测到这个条件的,此时read
会返回 0。
我总算知道在学校作 oj 题时,为何要把
while(scanf("%s", str[i]))
写成while(scanf("%s", str[i]) != EOF)
了。
共享文件的方法不少,可是共享文件的概念比较晦涩难懂。
系统内核使用三个相关的数据结构来表示打开的文件,分别为:描述符表、文件表、v-node 表。每一个进程都有一张描述符表,其表项是由进程打开的文件描述符来索引的;文件表表示全部的打开文件的集合,全部进程共享这个表,关闭一个描述符会减小相应的文件表表项中的引用计数,当引用计数为零时,内核就睡删除对应的表项(是否是和垃圾回收机制很像?);v-node表 也是全部进程共享的,表中包含了用户组、大小等不少信息。
如上图所示,是一个进程打开了两个不一样的文件的样子,这种状况下没有共享文件;而若是以同一个 filename 调用 open 函数两次,就会发生共享文件的状况,其关键思想是每一个描述符都有它本身的文件位置,以下图所示。
有了上面的基础,那理解子进程如何继承父进程打开的文件就容易的多了,直观展现出来就是下面这个样子。
最后来看一个简单的题:下面程序的输出是什么?
int main() {
int fd1, fd2;
fd1 = Open("foo.txt", O_RDONLY, 0);
Close(fd1);
fd2 = Open("baz.txt", O_RDONLY, 0);
printf("fd2 = %d\n", fd2);
exit(0);
}
复制代码
Unix 进程生命周期开始,打开的前三个描述符已经被使用了,而 open 函数老是返回最低的未打开的描述符,因此第一次调用 open 函数会返回 3,调用 close 函数会释放描述符 3,因此最后对 open 函数的调用仍是会返回 3,即程序输出是:fd2 = 3
。
在学生时代,听到 I/O、文件等名词时,下意识就认定了对方是个厉害的角色,原本觉得本身也会能操做文件后就不会有这种感受了,而实际状况依旧如此,听到老程序员谈到 socket 等名词时,依旧充满了景仰之情。
团队老员工告诉我,不少工具看源码,都是同样的原理,应该好好看下 socket 编程,读完这一章,我貌似有点明白同事的意思了。