zoukankan      html  css  js  c++  java
  • OSLab文件描述符

    日期:2019/3/24

    内容:Linux文件描述符。

    一、基本概念

    • 文件描述符(File Descriptor)

    一个非负整数。应用程序利用文件描述符来访问文件。打开现存文件或新建文件时,内核会返回一个文件描述符。读写文件也需要使用文件描述符来指定待读写的文件。

    二、文件读写

    • 头文件的解析

    sys/stat.h

    status

    fcntl.h

    file control

    • int flags参数取值

    必须取值:O_RDONLY、O_WRONLY、O_RDWR。

    可选取值(两种取值通过 | 组合)

    O_APPEND

    每次写操作都写入文件的末尾

    O_CREAT

    如果指定文件不存在,则创建这个文件

    O_EXCL

    如果要创建的文件已存在,则返回-1,并且修改errno的值

    O_TRUNC

    如果文件存在,并且以只写/读写方式打开,则清空文件全部内容(即将其长度截短为0)(truncate,截断)

    O_NOCTTY

    如果路径名指向终端设备,不要把这个设备用作控制终端。

    O_NONBLOCK

    如果路径名指向FIFO/块文件/字符文件,则把文件的打开和后继I/O

    • mode_t mode参数

    参    数

    说    明

    参    数

    说    明

    S_IRUSR

    所有者拥有读权限

    S_IXGRP

    群组拥有执行权限

    S_IWUSR

    所有者拥有写权限

    S_IROTH

    其他用户拥有读权限

    S_IXUSR

    所有者拥有执行权限

    S_IWOTH

    其他用户拥有写权限

    S_IRGRP

    群组拥有读权限

    S_IXOTH

    其他用户拥有执行权限

    S_IWGRP

    群组拥有写权限

       

    (数字表示法,例如chmod -r dir 777,777表示最高权限)

    • 与文件相关的函数

    int open(char *path, int flags, mode_t mode);

    ·mode是文件权限标志

    ·返回值:成功则返回文件描述符,否则返回-1

    int openat(int dirfd, const char *pathname, int flags, mode_t mode);

    ·用到时再补充

    int read(int fd, void *buf, size_t size);

    ·返回值成功返回读取的字节数,出错返回-1并设置errno,如果在调read之前已到达文件末尾,则这次read返回0。

    ·注意判断返回值与参数size的大小关系

    int write(int fd, void *buf, size_t size);

    ·返回实际写入的字节数,当有错误发生时则返回-1。

    三、内核与文件管理

    • 索引节点(Index Node, inode)

      >>索引节点保存了文件的元信息数据,包括:文件大小,磁盘位置,创建和修改时间,所有者,访问权限。

    • file结构体

      >>一个file结构体表示一个打开的文件。

      >>其包含的信息:对应的inode,文件当前的访问位置(即读写位置),打开模式,etc

    • 文件描述符表

      >>实质是一个数组,数组的元素类型是指针,指针指向一个file结构体。上文所述fd是数组下标。

      >>用于保存被打开的文件。

      >>内核打开文件时,分配一个file结构体表示被打开的文件,将该file结构体指针保存在文件描述符表中。

    • 打开文件的过程
    1. 找到文件对应的索引节点inode
    2. 分配一个file结构体
    3. file结构体的inode字段指向第1步的inode
    4. file结构体的文件访问位置字段初始化为0
    5. 从文件描述符表中找一个空闲项,指向第2步的file结构体,返回该空闲项在数组中的的下标,即fd

     

    • 进程控制块(PCB)

      >>进程控制块是操作系统表示进程状态的数据结构。

      >>存放用于描述进程情况及控制进程运行所需的全部信息。包括:进程标识信息(PID)、处理机状态、进程调度信息、打开文件列表(即上文所述的文件描述符表,记录进程打开的文件)

      >>私有的文件描述符表

      >>>>文件描述符表对进程来说是私有的: 每个进程都有一个私有的文件描述符表; 操作系统有N个进程,则对应有N张文件描述符表。

      >>>>两个进程打开不同的文件,文件描述符可能是相同的。进程A打开文件a.txt,open返回值是3;进程B打开文件b.txt,open返回值也可能是3。

      >>实例代码

    aopen.c

    bopen.c

    #include <stdio.h>

    #include <fcntl.h>

    #include <sys/types.h>

    #include <sys/stat.h>

    #include <unistd.h>

    int main()

    {

        int fd = open("aopen.c", O_RDONLY);

        printf("a fd = %d ", fd);

        close(fd);

        return 0;

    }

    #include <stdio.h>

    #include <fcntl.h>

    #include <sys/types.h>

    #include <sys/stat.h>

    #include <unistd.h>

    int main()

    {

        int fd = open("bopen.c", O_RDONLY);

        printf("b fd = %d ", fd);

        close(fd);

        return 0;

    }

     

    • 三个标准文件(stdin、stdout、stderr)

      >>一切皆文件

      >>stdin:标准输入文件,通常对应终端的键盘,进程的文件描述符表第0项

      >>stdout:标准输出文件,通常对应终端屏幕,进程的文件描述符表第1项

      >>stderr:标准错误输出文件,通常对应终端屏幕,进程的文件描述符表第2项

      >>如果关闭2号文件stderr(即close(2)),再打开一个新文件fd,那么fd=2。

      >>实例代码

    int main()

    {

        char buf[256] = { 0 };

        int len = read(0, buf, sizeof(buf));

        write(1, buf, len);

        return 0;

    }

     

    四、重定向

    • 系统调用fork

      >>创建一个子进程:为子进程创建一个独立的地址空间;为子进程创建一个独立的文件描述符表。

      >>子进程复制父进程属性:代码段、数据段的内容;文件描述符表

    • 系统调用dup

      >>原型:int dup(int oldfd);

      >>功能:通过复制文件描述符oldfd,创建一个新的文件描述符newfd。newfd和oldfd指向相同的文件。

      >>返回值:成功newfd,失败-1(如oldfd不是有效的描述符时)

      >>成功后,newfd与oldfd不会共享fd_flags(例如the close-on-exec flag,close(newfd)之后,oldfd依然有效)

      >>代码与图解

    代码

    dup前

    dup后

    int main()

    {

        int testfd = 6;

        int newtestfd = dup(testfd);

        printf("newtestfd = %d ", newtestfd);

     

        int oldfd = 2;

        const char *str = "Hello World from str... ";

        int newfd = dup(oldfd);

        write(newfd, str, strlen(str));

        close(newfd);

        write(oldfd, str, strlen(str));

        return 0;

    }

     

    • 系统调用dup2

      >>原型:int dup2(int old, int new)

      >>功能:用new指定新描述符的值,如果new本身已经打开了,则会先将其关闭再将new指向old,最后返回new。如果old等于new,则返回new,并不关闭它。(简记:关new指old,可用于输出的重定向)

      >>返回值:成功返回新复制的fd(即newfd),失败-1。如果old无效,返回-1。

      >>重定向实例代码(将stderr重定向到文件log)

    代码

    图解

    int main()

    {

        int fd = open("log", O_RDWR | O_CREAT);

        dup2(fd, 2);

        char *str = "Hello World... ";

        write(2, str, strlen(str));

        close(fd);

        return 0;

    }

     

    • 系统调用pipe(可以实现父子进程之间的通信

      >>原型:int pipe(int fd[2])

      >>功能:创建一个可读写的管道,管道具备读端和写端。fd[0]是pipe的读端,fd[1]是管道的写端。

      >>返回值:成功0,失败非0。

      >>注意:管道中的buf一旦被读取,就没有了。(即:数据只能读取一次)

      >>实例1

    int main()

    {

        int fd[2] = {-1, -1};

        pipe(fd);

        const char *str = "Hello world... ";

        char buf[256];

        printf("read = %d, write = %d ", fd[0], fd[1]);

        write(fd[1], str, strlen(str));

        read(fd[0], buf, 255);

        printf("buf = %s ", buf);

        close(fd[0]);

        close(fd[1]);

    }

     

    >>实例2(实现命令:cat pipe.c | wc)

    int main()

    {

        int fd[2];

        int pid;

        int status = 0;

        char buf[1024 * 2] = "";

        pipe(fd); //must be at the front of fork()

        pid = fork();

     

        if (pid == 0)

        {

            dup2(fd[1], 1); //stdout points to wirte pipe

            close(fd[0]);

            close(fd[1]);

            execlp("cat", "cat", "pipe2.c", NULL);

            exit(-1);

        }

     

        wait(&status);

        if(status != 0)

            perror("execlp error... ");

     

        dup2(fd[0], 0); //stdin points to read pipe

        close(fd[0]);

        close(fd[1]);

        //read(0, buf, sizeof(buf) - 1);

        execlp("wc", "wc", NULL);

        printf("Are program getting here? ");

        return 0;

    }

    运行结果

    如果把父进程中execlp前面的read加上

    s

    分析:

    ·pipe调用必须在fork之前,否则不会共享pipe

    ·父子进程中注意关闭不需要的fd[i](因为通常是一个进程读,一个进程写)

    ·pipe中buf的数据只能读取一次

    图解:

  • 相关阅读:
    (转)mysql 中的 latch锁和Tlock(事务锁), DML加锁规则,以及死锁分析
    改变主库sync_binlog,减小主从同步延时
    windows10上使用SourceInsight阅读mysql源码
    centos6.5安装systemstap
    centos6.5编译调试mysql-5.7.18
    事物特性
    Union和union all区别?
    Join(inner、left、right)的区别?
    hashhashmaphashTablehashSet
    String、StringBuffer、StringBuilder区别
  • 原文地址:https://www.cnblogs.com/sinkinben/p/10808589.html
Copyright © 2011-2022 走看看