系统级 I/O

    文件对于咱们来讲,貌似再普通不过了,windows 使用扩展名来区分不一样的文件,咱们接触了 gif、docx、pdf、mp三、mp四、exe 等等好多好多不一样的文件,可是它们在磁盘中都长一个样子,对于内核而言,没有什么区别。node

基本概念

    文件这个概念就是对 I/O设备 的抽象,一个文件就是一串 m 个字节的序列,全部的 I/O 设备(网络、磁盘、终端)都被模型化为文件,全部的输入输出都被当作对相应文件的读和写来执行。linux

image

    稍微特殊一点的是文件目录吧,目录也是一个文件,它是包含一组连接的文件,其中每一个连接都将一个文件名映射到一个文件,这个文件也多是另外一个目录。git

    每一个目录至少会有两个条目,.是到该目录自身的连接;..是到父目录的连接,你可使用mkdir命令建立一个目录,而后查看其内容观察观察,linux 用ls查看,windows 用dir程序员

image

    每一个进程都有一个当前工做目录,linux 是以/做为根目录的,因此绝对路径就是一个/开始,以下图所示,hello.c 的绝对路径名即为:/home/droh/hello.c;若是进程的当前工做目录为:/home/droh,那么 hello.c 的相对路径就为:./hello.c,而要是工做目录为:/home/bryant,那么相对路径名则为:../home/droh/hello.cgithub

image

    咱们可使用open函数来打开一个文件,它的做用是把文件名转换为一个文件描述符,这个描述符是一个小的非负整数,在后续对此文件的全部操做中标识这个文件。open函数返回的描述符老是在进程中当前没有打开的最小描述符。shell

    Linux shell 建立的每一个进程开始都有三个打开的文件:标准输入(描述符为 0)、标准输出(描述符为 1)、标准错误(描述符为 2)。编程

    open函数有三个入参,分别为filename, flags, mode,flags 指明了如何访问这个文件,即只读、只写、可读写;mode 指明了新文件的访问权限位,它经过与进程自带的umask进行运算来得到文件的访问权限,这个运算时:mode & ~umaskwindows

    读文件是从描述符为 fd 的当前文件位置复制 n 个字节到内存位置 buf,写文件则是把内存中的字节复制到当前文件中,系统中提供了 readwrite函数来提供相应的功能。网络

    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表 也是全部进程共享的,表中包含了用户组、大小等不少信息。

image

    如上图所示,是一个进程打开了两个不一样的文件的样子,这种状况下没有共享文件;而若是以同一个 filename 调用 open 函数两次,就会发生共享文件的状况,其关键思想是每一个描述符都有它本身的文件位置,以下图所示。

image

    有了上面的基础,那理解子进程如何继承父进程打开的文件就容易的多了,直观展现出来就是下面这个样子。

image

    最后来看一个简单的题:下面程序的输出是什么?

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 编程,读完这一章,我貌似有点明白同事的意思了。

相关文章
相关标签/搜索