日期: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\n", 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\n", 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\n", newtestfd);

 

    int oldfd = 2;

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

    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...\n";

    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...\n";

    char buf[256];

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

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

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

    printf("buf = %s\n", 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...\n");

 

    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?\n");

    return
0;

}

运行结果

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

s

分析:

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

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

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

图解:

OSLab文件描述符的更多相关文章

  1. 文件描述符、文件表项指针、inode节点的关系

    内核使用3种数据结构表示打开的文件,他们之间的关系决定了在文件共享方面一个进程对另一个进程的影响. (1) 每个进程在进程表中都有一个纪录项,纪录项中包含一张打开文件描述符表,每个文件描述符各占一项, ...

  2. Linux内核笔记--深入理解文件描述符

    内核版本:linux-2.6.11 文件描述符(file descriptor)在Linux编程里随处可见,设备读写.网络通信.进程通信,fd可谓是关键中的关键. 深入理解可以增加我们使用它的信心. ...

  3. Linux 利用进程打开的文件描述符(/proc)恢复被误删文件

    Linux 利用进程打开的文件描述符(/proc)恢复被误删文件 在 windows 上删除文件时,如果文件还在使用中,会提示一个错误:但是在 linux 上删除文件时,无论文件是否在使用中,甚至是还 ...

  4. Linux 文件描述符和重定向

    200 ? "200px" : this.width)!important;} --> 介绍 文件描述符是与文件输入.输出相关联的整数,在编写脚本时会经常使用标准的文件描述符 ...

  5. linux专题一之文件描述符、重定向、管道符、tee命令

    本节讨论一下几个问题: 1. 文件描述符. 2. 重定向. 3. 管道符 4. tee的用法. 1. 文件描述符. 在linux系统中一切皆文件.文件夹和设备都是文件.如何用来区别不同的文件呢?这里的 ...

  6. CentOS最大文件描述符限制更改

    系统级的限制:/proc/sys/fs/file-max中设定了系统最大能打开的文件数. 查看该值可以用如下方式: [root@#panda ~]# cat /proc/sys/fs/file-max ...

  7. linux文件描述符open file descriptors与open files的区别

    一个文件被打开,也可能没有文件描述符,比如current working diretories,memory mapped files and executable text files ;losf可 ...

  8. linux文件描述符数量的坑

    ulimit -n  查看 单进程或线程,可打开的最大文件描述符数 通过ulimit -n 10240 设置文件描述符数: (当前shell生效,这真是个坑啊) 永久生效:(需要重启系统,也是个坑,好 ...

  9. Shell重定向文件描述符

    #!/bin/bash      最近在看shell,各种困惑,不过解决困惑的感觉还是很不错的.废话少说,linux中使用文件描述符来标识每个文件对象.文件描述符为一个非负整数,可以唯一标识会话中打开 ...

随机推荐

  1. centos6.5上配置apache + mysql + php4.4.9 + eaccelerator-0.9.5 + postgresql-8.3.13 备忘

    1.apache + mysql 直接利用 yum 安装 yum -y install httpd httpd-devel mysql mysql-server httpd-manual mod_pe ...

  2. log4j日志整合输出(slf4j+commonslog+log4j+jdklogger)

    log4j日志整合输出(slf4j+commonslog+log4j+jdklogger) 博客分类: 日志   J2EE项目中,经常会用到很多第三方的开源组件和软件,这些组件都使用各自的日志组件,比 ...

  3. cxf 介绍

    CXF 编辑     目录 1Apache CXF 简介 关于Apache CXF 功能特性 项目目标 2Apache CXF特点 灵活部署 支持多种编程语言 代码生成     1Apache CXF ...

  4. 拖拽文件实现无刷新上传,支持2G文件

    客户端 用HTML5:jQuery File Upload http://blueimp.github.io/jQuery-File-Upload/basic-plus.html API https: ...

  5. CRC在线计算工具

    http://www.lammertbies.nl/comm/info/crc-calculation.html

  6. kbmmw 中JSON 操作入门

    现在各种系统中JSON 用的越来越多.delphi 也自身支持JSON 处理. 今天简要说一下kbmmw 内部如何使用和操作JSON. kbmmw 中json的操作是以TkbmMWJSONStream ...

  7. vsftpd只能连接不能上传文件问题

    Centos7 记得很清楚,vsftpd安装后,不需要配置,本地用户就可以正常使用(登录.上传.下载) 这次配的就是不行,另起了个虚拟机,装了下,就是不需要配置,但是在一台机上,就是不行,只能登录,下 ...

  8. CEdit控件[转]

    1.CButton.CEdit等从CWnd继承了重要的功能: 使用CWnd::SetWindowText和CWnd::GetWindowText可以设置和获得窗口或控件上的文本.CWnd::SetFo ...

  9. main.cpp

    /*main.cpp * *The starting point of the network simulator *-Include all network header files *-initi ...

  10. lnmp源码编译安装zabbix

    软件安装 Mysql 安装 tar xf mysql-5.7.13-1.el6.x86_64.rpm-bundle.tar -C mysql rpm -e --nodeps  mysql-libs-5 ...