C语言K&R圣经笔记 8.1文件描述符 8.2低级IO

第八章 UNIX 系统接口

UNIX 操作系统通过一系列“系统调用”来对外提供服务,实际上,系统调用是操作系统内可以被用户程序调用的函数。本章描述怎样在 C 程序中使用一些最重要的系统调用。如果你使用 UNIX,本章应当会有直接的帮助,因为有时需要使用系统调用以得到最大化的效率,或访问库中没有的一些功能。然而,即使你在其他操作系统上使用 C 语言,通过研究这些样例,你也能够对 C 语言编程有更深入的理解;尽管细节不同,但类似的代码在任何系统上都能找到。由于在很多情况下 ANSI C 库是以 UNIX 功能为原型的,这些代码也能帮助你理解库。

本章分为三个主要部分:输入输出、文件系统和内存分配。前两部分假定读者对 UNIX 系统的外部特性有一定的熟悉。

第七章关注的是跨操作系统的统一输入/输出接口。在任何特定的系统上,标准库必须用该主机系统提供的功能来写。下面几节我们将描述 UNIX 的输入输出系统调用,并展示如何使用它们来实现部分的标准库。

8.1 文件描述符


在 UNIX 操作系统中,所有输入输出都是通过文件读写来完成的,因为所有的外部设备,即使是键盘和屏幕,也是文件系统中的文件。这意味着一个同构的接口就能处理程序和外设之间的所有通信。

在大多数情况下,在你读写文件之前,必须把你的意图告诉系统,这个过程叫做“打开”文件。如果你想要写文件,可能还有必要创建文件或者丢弃它之前的内容。系统会校验你是否有权限做(文件存在吗?你有权限访问它吗?),如果一切正常,会向程序返回一个称为“文件描述符”的非负小整数。(文件描述符类似标准库使用的文件指针,或是 MS-DOS 的文件句柄。)被打开的文件,其所有相关信息都由系统维护;用户程序只通过文件描述符来引用该文件。

由于涉及键盘和屏幕的输入输出非常普遍,为了使其更方便,系统做了一些特殊的安排。当命令解释器(“shell”)执行程序时,文件描述符分别为 0、1、2 的三个文件被打开,称为标准输入、标准输出和标准错误。如果程序读 0 并且写 1 和 2,则它可以进行输入输出而不用关心如何打开文件。

程序的用户可以使用 < 和 > 将 IO 重定向为来自文件或写到文件,如

prog <infile >outfile


在这种情况下,shell 把文件描述符0和1从默认分配改变为对应的文件。通常情况下,文件描述符 2 还是保持与屏幕的连接,因此错误消息能往屏幕输出。在输入或输出关联到管道的情况下,也能得到类似的观察结果。在所有情况下,文件分配是被 shell 而不是被程序修改的。只要程序使用0作为输入,而1和2作为输出,它就不知道自己的输入来自哪里,而输出去了哪里。

8.2 低级IO - read和write


输入和输出使用 read 和 write 系统调用,C 程序可以通过两个叫做 read 和 write 的函数来访问它们。两个函数的第一个参数都是文件描述符。第二个参数是你程序中的一个字符数组,表示数据要输出到哪里,或来自哪里。第三个参数是要传输的字节数量。

int n_read = read(int fd, char *buf, int n);
int n_written = write(int fd, char *buf, int n);


每次调用返回已传输的字节数量。在读时,返回的字节数可能比请求的数量少。返回值为 0 字节说明文件结束,而 -1 表示出现了某种错误。在写时,返回值是写入的数量;如果不等于请求写入的数量,则说明发生了错误。

在一次调用中可能返回任意数量的字节。最常见的值是 1,这意味着每次一个字符(“无缓存”),而像 1024 和 4096 这样的数字对应着外部设备的一个物理块的大小。更大的字节数会更高效,因为系统调用的次数会更少。

把这些内容拼凑起来,我们就可以写个简单的程序来将其输入拷贝到输出,等价于在第一章写的文件拷贝程序。这个程序可以从任意的输入拷贝到任意的输出,因为输入和输出可以重定向到任意文件或设备。

#include "syscalls.h"

main()    /* 拷贝输入到输出 */
{
    char buf[BUFSIZ];
    int n;
    
    while ((n = read(0, buf, BUFSIZ)) > 0)
        write(1, buf, n);
    return 0;
}

我们将这些系统调用的函数原型集中到一个叫做 syscalls.h 的头文件内,这样就能在本章的程序中包含使用了。不过,这个文件名不是标准。

参数 BUFSIZ 也是定义在 syscalls.h 中的,它的取值与本地系统适配。如果文件大小不是 BUFSIZ 的倍数,某一次 read 就会返回一个较小的字节数,来供 write 写入;对 read 的下一次调用会返回零。

看看如何使用 read 和 write 来构造如 getchar,putchar 等高级例程,是有指导意义的。例如,下面这个版本的 getchar 进行无缓冲的输出,每次从标准输入读取一个字符。

#include "syscalls.h"

/* getchar:无缓冲的单个字符输入 */
int getchar(void)
{
    char c;

    return (read(0, &c, 1)) == 1 ? (unsigned char) c : EOF;
}

c 必须是 char,因为 read 需要字符指针。在返回语句中,把 c 强制转换为 unsigned char,避免了符号扩展带来的问题。

第二个版本的 getchar 采用大块的输入,并每次输出一个字符。

#include "syscalls.h"

/* getchar:简单的缓冲版本 */
int getchar(void)
{
    static char buf[BUFSIZ];
    static char *bufp = buf;
    static int n = 0;

    if (n == 0) {    /* 缓冲区为空 */
        n = read(0, buf, sizeof buf);
        bufp = buf;
    }
    return (--n >= 0) ? (unsigned char) *bufp++ : EOF;
}

如果这两个版本的 getchar 在编译时也包含了 <stdion.h> ,那么当标准库的 getchar 是用宏来实现时,我们代码里有必要对 getchar 这个名字做 #undef 。

相关推荐

  1. C语言K&R圣经笔记 8.1文件描述符 8.2低级IO

    2024-02-22 07:52:01       27 阅读
  2. IOS面试题object-c 81-90

    2024-02-22 07:52:01       21 阅读
  3. C语言K&R圣经笔记 4.1函数基础

    2024-02-22 07:52:01       36 阅读
  4. C语言K&R圣经笔记 4.3 外部变量

    2024-02-22 07:52:01       24 阅读
  5. C语言K&R圣经笔记 5.11函数指针

    2024-02-22 07:52:01       30 阅读
  6. LeetCode //C - 81. Search in Rotated Sorted Array II

    2024-02-22 07:52:01       10 阅读

最近更新

  1. TCP协议是安全的吗?

    2024-02-22 07:52:01       16 阅读
  2. 阿里云服务器执行yum,一直下载docker-ce-stable失败

    2024-02-22 07:52:01       16 阅读
  3. 【Python教程】压缩PDF文件大小

    2024-02-22 07:52:01       15 阅读
  4. 通过文章id递归查询所有评论(xml)

    2024-02-22 07:52:01       18 阅读

热门阅读

  1. Gson 库的使用

    2024-02-22 07:52:01       24 阅读
  2. Linux常见命令 | 文件传输命令scp

    2024-02-22 07:52:01       26 阅读
  3. [opencvsharp]将Mat转float数组

    2024-02-22 07:52:01       30 阅读
  4. CSS总结

    CSS总结

    2024-02-22 07:52:01      28 阅读
  5. scss导出颜色变量为空对象

    2024-02-22 07:52:01       28 阅读
  6. 事件流 事件委托

    2024-02-22 07:52:01       33 阅读
  7. Vue中v-model的原理

    2024-02-22 07:52:01       26 阅读
  8. docker入门介绍

    2024-02-22 07:52:01       32 阅读
  9. Backend - Docker 离线卸载

    2024-02-22 07:52:01       28 阅读
  10. Vue3利用父子组件实现字典

    2024-02-22 07:52:01       32 阅读
  11. linux系统离线安装docker服务教程

    2024-02-22 07:52:01       34 阅读
  12. 深度学习基础之《TensorFlow框架(5)—会话》

    2024-02-22 07:52:01       30 阅读
  13. select滑动分页请求数据

    2024-02-22 07:52:01       28 阅读