其他分享
首页 > 其他分享> > CSAPP - 第十章 系统级 I/O 读书笔记

CSAPP - 第十章 系统级 I/O 读书笔记

作者:互联网

权当是把书搬到 md 上面

CSAPP Chapter 10 系统级 I/O

标准 IO 库没有提供读取 元文件 数据的方式,例如文件大小或文件创建时间

10.1 Unix I/O

一个 Linux 文件 是一个 m 个字节的序列:

\[B_0,B_1,...,B_k,...,B_{m-1} \]

所有 IO 设备(网络、磁盘和终端)都被模型化为文件,而所有的输入和输出都被当成对相应文件的 来执行。

Linux 将设备映射为文件 的这种方式,允许 Linux 内核引出一个简单、低级的应用接口,称为 Unix I/O:

10.2 文件

10.2.1 文件类型

Linux 文件都有一个 类型 type 表名它在系统中的角色:

10.2.2 目录层次结构

Linux 内核将所有文件都同一组织成一个 目录层次结构(directory hierarchy)

由名为 / (斜杠) 的根目录确定

系统中的每个文件都是根目录的直接或间接后代

下图是 Linux 目录层次的一部分,尾部有斜杠表示是目录

![image-20211023150852386](D:\Documents\Study Data\Notes\深入理解计算机系统\Chapter 10 系统级 IO.assets\image-20211023150852386.png)

每个进程都有一个 当前工作目录(current working directory) 来确定其在 目录层次结构中的当前位置

10.3 打开和关闭文件

进程通过调用 open 函数 打开一个已存在的文件或者创建一个新文件:

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

int open(char *filename, int flags, mode_t mode);
// 返回 :若成功则为新文件描述符,若出错则为 -1

open 函数将 filename 转换为一个文件描述符,且返回描述符数字。

返回的描述符数字总是在进程中当前没有打开的最小描述符。

flags 参数

下面代码说明: 如何以读的方式打开一个已存在的文件

fd = Open("foo.txt", O_RDONLY, 0)

flags 参数也可以是一个或者更多位掩码的或,为写提供给一些额外的指示:

下面代码说明: 打开一个已存在文件,并在后面添加一些数据:

fd = Open("foo.txt", O_WRONLY|O_APPEND, 0)

| 表示管道:前面的结果作为后面的输入

mode 参数

作为上下文的一部分,每个进程都有一个 umask,通过调用 umask 函数来设置

当进程通过带某个 mode 参数的 open 函数来创建一个新文件时,文件的访问权限位被设置为 mode & ~ umask

假设给定下面的 mode 和 umask 默认值:

#define DEF_MODE S_IRUSR|S_IWUSR|S_IGRP|S_IWGRP|S_IROTH|S_IWOTH

#define DEF_UMASK S_IWGRP|S_IWOTH

接下来,下面的代码片段创建一个新文件,文件的拥有者有读写权限,而其他所有的用户都有读写权限

umask(DEF_UMASK);
fd = Open("foo.txt", O_CREAT|O_TRUNC|O_WRONLY, DEF_MODE);

最后,进程通过调用 close 函数来关闭一个打开的文件

#include <unistd.h>
int close(int fd);
// 返回:若成功则为 0, 若出错则为 -1

练习

下面程序的输出是什么

#include "csapp.h"

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);
}

输出什么? 2 吗

10.4 读和写文件

应用程序调用 readwrite 函数 输入输出

#include <unistd.h>
// fd 文件描述符        *buf 内存位置		n 大小
ssize_t read(int fd, void *buf, size_t n);
// 返回: 若成功则为读的字节数,若 EOF(end of file) 则为0,若出错则为 -1.

ssize_t write(int fd, const void *buf, size_t n);
// 返回: 若成功则为写的字节数,若出错则为 -1.

10.4.1 read 函数

10.4.2 write 函数

示例代码

code/io/cpstdin.c

#include "csapp.h"

int main(void) 
{
    char c;
    
    while(Read(STDIN_FILENO, &c, 1) != 0)
        Write(STDOUT_FILENO, &c, 1);
    exit(0);
}

上述代码使用 read 和 write 调用一次一个字节地从标准输入复制到标准输出。

调用 lseek 函数,应用程序能够显式地修改当前文件的位置(书中没讲)

10.4.3 不足值 short count

在某些情况下, read 和write 传送的字节比应用程序要求的要少。这些不足值( short count )不表示有错误

10.4 读和写文件

#include <unistd.h>
// fd file description,
// 从描述符为 fd 的当前文件位置复制最多 n 个字节到内存位置 buf
// 返回 -1 表示错误
// 返回 0 表示 EOF    end of file
// 否则返回实际传送的字节量
ssize_t read(int fd, void *buf, size_t n);

// 从内存位置 buf 复制至多 n 个字节到描述符 fd 的当前文件位置
// 成功则返回写的字节数
// 失败则返回 -1
ssize_t write(int fd, const void *buf, size_t n);

程序 code/io/cpstdin.c

#include "csapp.h"

int main(void) {
    char c;
    // 如果 从 文件描述符为 STDIN_FILENO 的位置复制 1 个 字节到内存位置 &c 成功
    while (Read(STDIN_FILENO, &c, 1) != 0)
        // 从内存位置 &c 复制 1 个字节到 描述符 为 fd 的当前文件位置
        Write(STDOUT_FILENO, &c, 1);
    exit(0);
}

上述程序表示:一次一个字节地从标准输入复制到标准输出

short count 不足值

有时候 read 和 write 传送的字节数比 app 要求的少,这些 不足值 short count 不表示有错误

也就是说,不足值是已经读到的文本

原因:

10.5 用 RIO 包健壮地读写

10.5.1 RIO

Robust I/O ,健壮的 I/O 包

自动处理上文的 不足值

RIO 提供两种函数

10.5.2 RIO 的无缓冲的输入输出函数

rio_readnrio_writen

10.5.2 RIO 的带缓冲的输入函数

如何计算文本中有多少行?

#include "csapp.h"

// 返回:无
// 将 描述符 fd 和 地址 rp 处的一个类型为 rio_t 的 读缓冲区 联系起来
void rio_readinitb(rio_t *rp, int fd);

// 成功返回读的字节数;EOF 返回 0,出错则返回 -1
ssize_t rio_readlineb(rio_t *rp, void *usrbuf, size_t maxlen);
ssize_t rio_readnb(rio_t *rp, void *usrbuf, size_t n);

rio_t 是一个 读缓冲区

每打开一个描述符,都会调用一次 rio_readinitb 函数 都会 将 描述符 fd 和 地址 rp 处的一个类型为 rio_t 的 读缓冲区 联系起来

rio_readlineb 函数从文件 rp 读出下一个文本行(包括结尾的换行符),将它复制到内存位置 usrbuf,并且用 NULL 字符来结束这个文本行。

rio_readlineb 函数最多读取 maxlen - 1 个字节,余下的 一个字符留给结尾的 NULL 字符。超过 maxlen - 1 字节的文本行被截断,并用一个 NULL 字符结束

rio_readnb 函数从文件 rp 最多读 n 个字节到内存位置 usrbuf

对同一描述符,对 rio_readlinebrio_readnb 的调用可以任意交叉进行,然而对于这些带缓冲的函数的调用不能和 无缓冲rio_readn 函数交叉使用

/**
 * 从标准输入复制一个文本到标准输出
 */
#include "csapp.h"

int main(int argc, char **argv)
{
    int n;
    rio_t rio;
    char buf[MAXLINE];
    
    Rio_readinitb(&rio, STDIN_FILENO);
    while((n = Rio_readlineb(&rio, buf, MAXLINE)) != 0)
        Rio_writen(STDOUT_FILENO, buf, n);
}

标签:文件,CSAPP,字节,读书笔记,read,第十章,rio,描述符,fd
来源: https://www.cnblogs.com/icewalnut/p/15564606.html