闇の光

读书笔记 经验感受

  博客园 :: 首页 :: 博问 :: 闪存 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理 ::

每个运行的程序,都被称之为一个进程,它们都有一个与之相关联的文件描述符。这些文件描述符是一些小的整数,你可以使用它们来访问打开的文件或设备,它们有效性的多少依赖于系统是如何配置的。当一个程序开始时,通常它拥有的三种描述符已经被打开,分别是:

  • 0: 标准输入

  • 1: 标准输出

  • 2:标准错误
  • 你可以通过使用open系统调用将其他的文件描述符同文件和设备关联起来,这样文件标识符就为自动打开的。不过,要允许你能够创建一些简单的程序则需使用write系统调用。

    下面我们将介绍一些常见的系统调用:

    write

    write系统调用为来自buf的第一个nbytes字节做好准备,从而将之写入到以fildes为文件标识符相关联的文件中。它将返回确切写入的字节数。如果在文件标识符中含有错误或者基础设备驱动器对数据块尺寸敏感的话,这都可能使得所需字节会少于nbytes。如果函数返回0,则意味着没有写入任何数据;如果返回-1,则表示在write调用中有一个错误,且该错误将被指定到全局变量errno中。

    下面是write系统调用的语法结构:

    #include <unistd.h>
    size_t write(int fildes, const void *buf, size_t nbytes);

    通过此语法结构,你就可以写你的第一个程序(simple_write.c):

    #include <unistd.h>
    #include <stdlib.h>

    int main()
    {
        if ((write(1, "Here is some data\n", 18)) != 18)
            write(2, "A write error has occurred on file descriptor 1\n", 46);

        exit (0);
    }

    该程序将从标准输出中打印一份信息。当一个程序退出时,所有打开的文件标识符将自动关闭,而不需要你明确地关闭它们。这虽然不是一个实例,不过你却涉及到了缓冲区输出。结果如下:

    $ ./simple_write
    Here is some data

    值得再次注意的一点是write可能会报告它所写入的字节少于你所要求的字节,这不是一个错误。在你自己的程序中,你可以通过查看errno来查明错误,并调用write写入所有剩余的数据。

    read

    read系统调用读取从以fildes为文件标识符相关联的文件的数据,直到数据大小为nbytes字节,之后放置它们到数据区域buf中。它将返回确切读取的数据字节数,可能会少于你所请求的数目。如果一个read调用返回0,表示没有读取任何数据,直接到达文件的结尾;同样,如果有错误在此调用上将促使它返回-1。

    read系统调用的语法结构:

    #include <unistd.h>
    size_t read(int fildes, void *buf, size_t nbytes);

    下面的程序simple_read.c复制了标准输入的第一个128字节的内容到标准输出,另外如果标准输入的内容少于128字节,程序将复制输入的全部内容。程序如下:

    #include <unistd.h>
    #include <stdlib.h>

    int main()
    {
        char buffer[128];
        int nread;

        nread = read(0, buffer, 128);
        if (nread == -1)
            write(2, "A read error has occurred\n", 26);

        if ((write(1, buffer, nread))!= nread)
            write(2, "A write error has occurred\n", 27);

        exit(0);
    }

    运行程序,你将得到如下结果:

    $ echo hello there | ./simple_read
    hello there
    $ ./simple_read < draft1.txt
    Files
    In this chapter we will be looking at files and directories and how to manipulate
    them. We will learn how to create files.
    $

    在第一个程序运行中,我们使用echo通过管道为程序创建了一些输入;而在第二个程序运行中,我们直接从一个文件将内容作为输入。在这种情况下,我们将看到文件draft1.txt的第一部分(128字节)出现在标准输出中。

    注意:输出的最后一行末尾所出现的shell提示符,这是因为在这个例子中,此行的128个字节中的字节不够组成一整行的数目。

    open

    要创建一个新的文件描述符,我们需要使用open系统调用。

    open系统调用的语法结构如下:

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

    int open(const char *path, int oflags);
    int open(const char *path, int oflags, mode_t mode);

    (严格来说,我们在依照POSIX标准的系统上使用open不需要包含sys/types.h和sys/stat.h,不过在有些UNIX系统中却有必要包含他们。)

    在简单的术语中,open建立一个访问路径到一个文件或设备上。如果成功,就返回一个可以被使用到read、write以及其他系统调用中的文件描述符。此文件描述符是独一无二的,且不能被其他可能正在运行的进程所分享。如果两个程序同时打开同一个文件,他们将保持不同的文件描述符。如果他们都写入同一个文件,则将数据继续写到他们不用的地方。他们的数据不是交插的,但是其中一个将重写另一个所写的数据。他们中的每一个都保持自己的想法,能够在此文件(偏移量)中读取或写入多远。你可以通过使用文件锁的方式来预防这种不想要的冲突,我们将在以后介绍。

    文件或设备的名字是通过参数path来被打开;而oflags参数被用来指定文件打开时所做的动作。oflages被指定作为一个通过访问模式以及其他可选模式的指令文件的组合。open调用必须指定下表中所列的文件访问模式之一:

    ModeDescription
    O_RDONLY打开为只读
    O_WRONLY打开为只写
    O_RDWR打开为可读可写

    此调用可能也包括下面所列的是在oflags参数中的可选模式的组合:

  • O_APPEND:放置写入的数据到文件的结尾。

  • O_TRUNC:设置文件的长度为零,丢弃所存在的内容。

  • O_CREAT:在mode中创建文件,如果有必要,同时给出许可。

  • O_EXCL:与O_CREAT一起使用,确保调用创建文件。
  • (open是单一的,也就是说它只能被一个函数调用,这就阻止了两个程序在同一时刻创建同一个文件。如果所要创建的文件已经存在,则open调用失败。)

    open所返回新的文件描述符,通常如果成功的话返回为非负整数;失败则返回-1,同时open也会设置全局标量errno来指示失败的理由。通常新的文件描述符为不在使用的最低标号的描述符,此特性在一些环境状况下是非常有用的。比如,一个程序关闭了它的标准输出,之后再次调用open,则文件描述符1将被重新使用,且标准输出将被有效地重定向到一个不同的文件或设备。

    还有一个在POSIX下被标准化的creat调用,不过它不被经常使用。creat不但创建一个文件,作为一种可能期待,同样也会将之打开。它是带有oflags的opean调用的等同物,相当于O_CREAT | O_WRONLY | O_TRUNC。

    任何正在运行的程序可以马上打开的文件数目是有限的。这个限制,通常由在limits.h中的常量OPEN_MAX所确定,根据系统的不同而不同,不过POSIX要求其至少为16。此外,该限制可以自己受支配于本机系统范围内的限制,这样就可以使得一个程序不能总是打开这么多的文件。在Linux中,此限制可以在运行时被改变,因此OPEN_MAX也就不是一个常量,一般都是从256开始。

    posted on 2008-04-07 15:12  taizi  阅读(310)  评论(0编辑  收藏  举报