[POSIX]文件系统(概述)

1.文件名由除系统目录分隔符(unix是/,windows是\)和空字符“\0”外的任意ASCII字符组成,现代系统很多还可以包含UNICODE字符,但是还是推荐使用传统的ASCII码命名.

2.目录不能创建硬链接.

3.文件描述符是一个非负数.

4.文件描述符(fd)是一个非负数,每个进程的fd之间不存在联系,每个进程都有一个注1|进程表项,每个进程调用打开文件系统调用时,进程表项就会增加一条,每条的id就是fd,fd只会按照“从0开始(每个进程从各自的0开始),返回最小的未用的fd”的这个规则返回fd(通常0到2会被系统标准输出输入占用),不同进程打开同一个文件,会有不同的fd返回.

5.一般shell程序会把0、1、2三个文件描述符标准化为shell程序的输入输出和错误输出,换句话说文件描述符0到2已经被这3个标准输入输出占用了.

POSIX也定义了三个常量代表了这三个数字包含在unistd.h头文件上

STDIN_FILENO 0
STDOUT_FILENO 1
STDERR_FILENO 2

 

代码示例:

#include <fcntl.h>
#include <unistd.h>

int main(void){
    char s_1[] = "my error.\n";
    write(STDERR_FILENO, s_1, 10);
    return 0;
}

此代码片段会在控制台,输出"my error.\n".

6.在shell中启动的进程写入STDOUT_FILENO和STDERR_FILENO会输出到屏幕,但是STDOUT_FILENO是带缓冲的IO,而STDERR_FILENO是即刻响应的.

7.由open和openat返回的文件描述符,一定是最小未用的文件描述符,利用这一特性,应用程序可以关闭掉标准的输入输出从而启动自行的输入输出.

代码示例:

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


int main(void){
    #define LEN 3
    char p_1[] = "./abc.txt";
    close(STDOUT_FILENO);
    int fd_1 = open(p_1, O_RDWR|O_APPEND|O_CREAT);
    
    printf("%d\n", fd_1);

    return 0;
}

以上代码关闭了标准输出,再把它重定向到了进程当前工作目录下的"./abc.txt"文件,这时进程的所有输出,都会被记录到"./abc.txt"上.

 8.具有缓冲的I/O,当输入或输出还不至于填满缓冲区时,实际只是I/O到缓冲区,只有在两种情况带缓冲的函数才会输出到设备:

     ---缓冲区被填满,缓冲区内容自动被刷出.

     ---程序正确退出(exit(0)),就算缓冲区不被填满也会被刷出.

 9.在shell终端,当键入回车键时终端进程实际上会执行两个动作.

    ---输入当前系统回车字符,至于输入什么字符通常是终端运行系统环境决定的,Unix体系为\n,Windows体系为\r\n.

    ---发送输入内容,也就是把输入内容写进STDIN.

10.一些带缓冲的,操作字符串的I/O函数,会在读取的返回内容长度n的位置自动填充\0,所以它读取的字符长度实际上只有n-1,例如fgets.

11.一些带缓冲的,操作字符串的I/O函数,在每次调用的时候会自动刷出缓冲区,就像调用fflush(fd)一样,例如fgets.

13.事实上,就算是不带缓冲的I/O函数,如read,write,也不会直接操作存储设备,传统unix系统实现在内核中设有缓冲区高速缓存或页高速缓存,大多数磁盘I/O都是通过缓冲区进行.举个例子,当进程A向文件A写入一个字符,内核并不会马上启动硬件把这一个字节写入存储设备,而是先填充在高速缓冲区,当进程B恰好向文件A读取此字符,内核会在缓冲区返回这个字符,同时会刷新缓冲区,这些操作,对于应用程序本身是感知不到的,除非你把存储设备同时绑定两个系统,如虚拟机和宿主共同访问一个存储设备,你会发现这个细节。

14.Unix还提供了一些函数来操作I/O高速缓存,通常还有一个守护进程定时将缓冲区的内容刷出缓冲区,详情请参考《Unix高级环境编程》3.13 函数sync、fsync和fdatasync.

15.当open/openat以O_APPEND标志打开文件时,调用lseek将不能改变write的偏移位,此时write的偏移位始终会在结尾开始,但是可以改变read的偏移位.

16.当文件不支持lseek时,可以调用lseek(STDIN_FILENO, 0, SEEK_CUR) == -1来判断,切忌一定要看是否等于-1,判断小于0没用,因为有些系统lseek支持设置为负值.

17.lseek仅将当前的文件偏移量记录在内核中,它并不引起任何I/O操作,因此在打开一个空文件,并设置偏移位,并不会为该文件写入任何东西,直到真正调用写入操作为止.

18.O_APPEND具有一定的特殊性,调用操作偏移位函数对write无效的根本原因是,每次write都会从v(i)-node获取文件长度作为自己的偏移量的特性,这就意味着你不能用无O_APPEND标志的fd设置一个跟文件长度对等的偏移位去模拟O_APPEND的行为.

19.当存在多个进程打开同一个文件,那么每个进程的这个fd会各自维护一个注2|文件表项,这个表包括文件状态标志(读,写,添,同步,非阻塞等信息)和当前文件偏移位,还有指向v(i)-node的指针。所以每个进程对于这个文件的操作偏移位不是共享的,这将会涉及到数据同步的问题,详情参考《Unix高级环境编程》3.10 文件共享 章节.

20.有些例外的情况,不同的进程或同进程不同的fd会引用同一个文件表,例如用来复制fd的dup函数和fork子进程时会发生这样的情况.

21.除root用户外,其余用户只能编辑属于自己名下的文件权限位,详情参考《Unix高级环境编程》4.9 章节.

22.只有root用户可以更新文件的所有者UID,而关于组GID,只有文件所有者可以修改组GID,详情参考《Unix高级环境编程》4.11 章节.

23.目录拥有者可以删除该目录下不属于自己或者不具备权限的文件.

24.一些文件系统不允许用户对目录创建硬链接(就算允许,也仅限于超级管理员),这是因为需要避免循环访问的原因,详情参考《Unix高级环境编程》4.15 章节.

25.一个文件只有一个iNode.

26.目录文件的block保存的是该目录下的目录项,目录项包含一个文件名和一个指向该文件iNode的指针.

27.重命名(或者改变文件路径),实际上是删除原有的目录项,新建一个指向该iNode的目录项.

28.硬链接会在block中创建目录项,目录项指向和这个文件的其他硬链接(如果有)同一个iNode上,关于这个iNode,每增加一个硬链接,就会在它的引用次数(stat.st_nlink)上加1,当删除该文件时,其实只是删除引用这个iNode的目录项,并且会在这个iNode的引用次数上减1,直到引用次数为0时,系统调用才会真正删除这个iNode,并回收它指向的block.

29.软连接则是一个单独的文件,拥有单独的iNode,这个文件的iNode表项中S_IFLNK表明这个文件是一个符号链接,iNode指向block,而block中包含了需要指向的文件的完整文件名(参考《Unix高级环境编程》4.14 章节),删除它并不影响它指向的文件.

30.硬链接要求文件在同一个文件系统,而软连接可以跨文件系统。只有超级用户才有权限为目录增加硬链接.

31.mkdir创建新文件目录时,会自动创建.和..这两个目录项,它们分别指向自己和自己的上一级,详情参考《Unix高级环境编程》4.21 章节.

 

 

注1|进程表项

标志 说明
close_on_exec 通常0(系统默认,exec时不关闭)或1(在exec时关闭),但是标准起见,POSIX定义了FD_CLOEXEC这个常量,代表在exec时关闭
文件指针 指向文件表项的指针

 

*如表格所示,Unix系统目前只定义了一种文件描述符标志

 

 

 

注2|文件表项

标志 说明
文件状态标志 打开文件的方式标志位
当前文件偏移量  
v(i)-node指针 指向v(i)-node的指针

 

 

 

 

 

posted @ 2018-09-15 18:44  yiyide266  阅读(4000)  评论(0编辑  收藏  举报