网络编程

网络编程 - FileDescriptor

2019-04-30  本文已影响0人  HRocky

inode

在描述FileDescriptor之前先来看一下inode。inode在我们后面的描述中会出现,所以首先需要弄清楚它是什么。

inode是index node的简写。它是Unix风格文件系统中的一种数据结构,用来描述比如说文件或目录这样的文件系统对象。每个inode存储对象数据的属性和磁盘块位置。文件系统对象属性可以包括元数据(上次更改的时间、访问、修改)以及所有者和权限数据。

inode中存储的是文件的信息,例如文件所有权、访问模式(读、写、执行权限)和文件类型等。文件的真实数据不存储在inode中,而是存储在称为"数据块"的地方;同时文件的名称也不存储在inode中。在许多类型的文件系统实现中,在创建文件系统时都会固定inode的最大数量,从而限制了文件系统可以容纳的最大文件数。对于文件系统中的inode,典型的分配启发式是总大小的1%。

每个文件都与inode相关联,inode由整数标识,通常称为i-number或inode号。

在设备的已知区域有一张inode表,inode编号就是这个表的索引。根据inode编号,内核的文件系统驱动程序可以访问inode内容,包括文件的位置,从而允许访问文件。

使用ls -i命令可以找到文件的inode号。ls-i命令在报表的第一列中打印 i-node编号。

rockydeMacBook-Pro:~ rocky$ ls -i
14373725 Applications       624053 Movies           14740502 account.txt
624013 Desktop              624055 Music            3800781 default-soapui-workspace.xml
623997 Documents            624057 Pictures         4320031342 node_modules
623999 Downloads            624059 Public           3800780 soapui-settings.xml
624001 Library              670752 Work

如果知道了文件的inode编号,那么可以使用下面的命令来查找文件:

rockydeMacBook-Pro:Work rocky$ ls -i
13583397 config_datasource.properties   13964509 npm-debug.log          13175726 workspace
2913703 document              674806 project
13822742 idea                 670756 software
rockydeMacBook-Pro:Work rocky$ find . -inum 13583397 -print
./config_datasource.properties
rockydeMacBook-Pro:Work rocky$ 

还可以根据文件的inode编号来删除文件:

rockydeMacBook-Pro:Work rocky$ touch 1.txt
rockydeMacBook-Pro:Work rocky$ ls
1.txt               document            npm-debug.log           software
config_datasource.properties    idea                project             workspace
rockydeMacBook-Pro:Work rocky$ ls -li
total 32
4321587203 -rw-r--r--   1 rocky  staff     0 Apr 29 20:41 1.txt
13583397 -rwxrwxrwx@  1 rocky  staff   138 Dec 10  2016 config_datasource.properties
2913703 drwxr-xr-x   8 rocky  staff   256 Nov 21  2016 document
13822742 drwxr-xr-x   4 rocky  staff   128 Mar 21  2017 idea
13964509 -rw-r--r--   1 rocky  staff  8720 Dec  5  2016 npm-debug.log
674806 drwxr-xr-x  20 rocky  staff   640 Apr 28 20:45 project
670756 drwxr-xr-x  23 rocky  staff   736 Apr 24 23:55 software
13175726 drwxr-xr-x   5 rocky  staff   160 Sep  1  2018 workspace
rockydeMacBook-Pro:Work rocky$ find . -inum 4321587203 -delete
rockydeMacBook-Pro:Work rocky$ ls -li
total 32
13583397 -rwxrwxrwx@  1 rocky  staff   138 Dec 10  2016 config_datasource.properties
2913703 drwxr-xr-x   8 rocky  staff   256 Nov 21  2016 document
13822742 drwxr-xr-x   4 rocky  staff   128 Mar 21  2017 idea
13964509 -rw-r--r--   1 rocky  staff  8720 Dec  5  2016 npm-debug.log
674806 drwxr-xr-x  20 rocky  staff   640 Apr 28 20:45 project
670756 drwxr-xr-x  23 rocky  staff   736 Apr 24 23:55 software
13175726 drwxr-xr-x   5 rocky  staff   160 Sep  1  2018 workspace
rockydeMacBook-Pro:Work rocky$ 

一些Unix风格的文件系统(如ReiserFS)省略了inode表,但必须存储等效的数据以提供等效的功能。该数据可以称为stat数据,参考向程序提供数据的stat系统调用。

文件名和目录含义:

hardlink

要了解hardlink是什么,重要的是要了解文件的标识是它的inode号,而不是它的名称。hardlink是一个指向inode的名称。这意味着如果file1有一个名为file2的hardlink,那么这两个文件都引用相同的inode。因此,当您为一个文件创建一个hardlink时,您真正要做的就是为一个inode添加一个新的名称。为此,请使用不带选项的ln命令。

# ls -l /home/bobbin/sync.sh  
-rw-r----- 1 root root 5 Apr 7 06:09 /home/bobbin/sync.sh
# ln /home/bobbin/sync.sh synchro

现在让我们比较两个文件:

# ls -il /home/bobbin/sync.sh synchro 
517333 -rw-r----- 2 root root 5 Apr 7 06:09 /home/bobbin/sync.sh
517333 -rw-r----- 2 root root 5 Apr 7 06:09 synchro

关于hard links的有趣之处在于原始文件和link之间没有差异:它们只是连接到同一inode的两个名称。

inode包含的属性

inode随复制、移动和删除而更改

当复制、移动或删除文件系统上的文件时,inode编号会怎样。

复制文件:CP分配一个空闲的inode编号,并在inode表中放置一个新条目。

### Check inode of existing file 
$ ls -il  myfile.txt
1150561 -rw-r--r-- 1 root root 0 Mar 10 01:06 myfile.txt

### Copy file with new name 
$ cp myfile.txt myfile_new.txt

### Check inode number of new file. Its changed 
$ ls -il myfile_new.txt
1150562 -rw-r--r-- 1 root root 0 Mar 10 01:09 myfile_new.txt

移动或重命名文件:如果目标与源文件系统相同,对inode编号没有影响,它只更改inode表中的时间戳。

### Check inode of existing file 
$ ls -il  myfile.txt
1150561 -rw-r--r-- 1 root root 0 Mar 10 01:06 myfile.txt

### Moved file to another directory 
$ mv myfile.txt /opt/

### Check inode number of moved file. No change in inode 
$ ls -il /opt/myfile.txt
1150561 -rw-r--r-- 1 root root 0 Mar 10 01:06 /opt/myfile.txt

删除一个文件:在Linux中删除一个文件,减少链接计数,释放的inode编号会被重用。

参考:https://tecadmin.net/what-is-inode-number-in-linux/

总结:文件通过文件名进行访问,但事实上,对于文件本身并不与文件名称直接相关联。相反,文件通过inode来访问,inode使用唯一的数值进行标志。该值称为inode编号(inode number),通常简写为i-number或者ino。一个inode存储文件关联的元数据,如它的修改时间戳、所有者、类型、长度以及文件的数据的地址—唯独没有文件名。inode既是Unix文件系统在磁盘上实际物理对象,也是Linux内核中的数据结构的概念实体。

file descriptor

可先阅读: 网络编程 - 文件系统,内核数据结构和打开文件

在Unix和相关的计算机操作系统中,文件描述符(FD)是用于访问文件或其他输入/输出资源(如管道或者网络套接字)的抽象指示符(句柄)。文件描述符构成POSIX应用程序编程接口的一部分。文件描述符是一个非负整数,通常在C编程语言中表示为int类型(保留负值以表示“无值”或错误条件)。

在Unix的传统实现中,文件描述符被索引进由内核维护的进程内文件描述符表(每进程拥有),然后索引为所有进程共享的表示已打开文件的表,称为文件表。此表记录打开文件(或其他资源)的模式:用于读取、写入、附加以及可能的其他模式。它还索引到第三个表,称为inode表,该表描述实际的底层文件。为了执行输入或输出,进程通过系统调用将文件描述符传递给内核,内核将代表进程访问文件。进程无法直接访问文件或inode表

在Linux上,进程中打开的一组文件描述符可以在路径/proc/pid/fd/下访问,其中PID是进程标识符。

在类似Unix的系统中,文件描述符可以引用在文件系统中命名的任何Unix文件类型。除了常规文件之外,它还包括目录、Blockand字符设备(也称为“特殊文件”)、Unix域套接字和命名管道。文件描述符还可以引用文件系统中通常不存在的其他对象,例如匿名管道和网络套接字。

文件描述符-简单 文件描述符-详细

示例-1:

#include <stdio.h>
#include <fcntl.h>

int main()
{
    char c;
    int fd = open("d:\\1.txt", O_RDONLY, 0);
    read(fd, &c, 1);

    printf("c = %c\n", c);
    exit(0);
}

输出:

c = 1

上面程序中fd就是打开文件的文件描述符,read方法执行系统调用将文件描述符作为参数传入进去。由内核执行后续的操作。

示例-2

#include <stdio.h>
#include <fcntl.h>

int main()
{
    int num;
    FILE *fptr;
    fptr = fopen("d:\\1.txt", "w");

    if(fptr == NULL) {
        printf("Error!");
        exit(1);
    }

    fprintf(fptr, "%d", 1213);
    fclose(fptr);

    return 0;
}

示例-2中我们可以看到没有看到文件描述符相关的信息,而是通过FILE类型的指针来进行操作。文件描述符是一个低级别的"句柄",用于标识内核级、Linux和其他类Unix系统中打开的文件(或套接字或其他什么)。C语言对文件描述符进行了包装,提出了文件指针的概念。

文件指针是C标准库级结构,用于表示文件。FILE包装了文件描述符,并添加缓冲和其他功能,以使I/O更容易。

标准文件描述符

在类似Unix的操作系统上,默认情况下,前三个文件描述符是STDIN(标准输入)、STDOUT(标准输出)和STDERR(标准错误)。

名称 数字 描述 缩写
Standard input 0 标准输入流文件描述符。在终端中,默认为来自用户的键盘输入 stdin
Standard output 1 标准输出流描述符。在终端中,默认为用户的屏幕 stdout
Standard error 2 标准错误流描述符。在终端中,默认为用户的屏幕 stderr

FileDescriptor类

上面示例-2中我们可以看到C语言提出了FILE这样的数据结构来包装文件描述符,使得我们能在一个较高的层次上进行文件的操作,而不用直接操作与内核有关的文件描述符。同样Java也进行了包装,提出了FileDescriptor类。

实例-1

@Test
public void test_1() throws IOException {
    FileOutputStream fileOutputStream1 = new FileOutputStream(FileDescriptor.out);
    fileOutputStream1.write(65);
}

运行上面的方法,可以看到屏幕中输出A。

public static final FileDescriptor out = standardStream(1);

FileDescriptor类中定义了上面我们说到的三个标准文件描述符,这里我们使用的就是标准输出文件描述符。持有这个文件描述符那么我们就持有了操作终端屏幕的能力。上面的代码中我们没有使用常用的System.out.println方法来执行向终端屏幕输出字符的功能,而是直接使用标准输出文件描述符来操作,实现相同的效果。

实例-2

@Test
public void test_2() throws IOException {
    File file = new File("D:/1.txt");
    FileOutputStream fileOutputStream = new FileOutputStream(file);
    FileDescriptor fileDescriptor = fileOutputStream.getFD();
    FileOutputStream fileOutputStream1 = new FileOutputStream(fileDescriptor);
    fileOutputStream1.write(65);
}

程序中打开的文件与文件描述符关联。上面的代码中创建了两个FileOutputStream对象,但是它们使用的是一个文件描述符,所以使用第二个FileOutputStream可以实现对1.txt文件的写操作。

上一篇 下一篇

猜你喜欢

热点阅读