系统级 I/O

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

基本概念

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

image

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

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

image

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

image

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

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

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

    读文件是从描述符为 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 编程,读完这一章,我貌似有点明白同事的意思了。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Guanngxu

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值