在linux系统中——一切都是文件。

1. 磁盘物理结构

磁盘的物理结构如下:

磁盘由很多盘面组成,而盘面上则是由很多同心圆环组成的磁道,每个磁道又被切割成许多扇区。所有磁盘面的同一个磁道构成一个柱面,同一柱面的所有磁道写完后,才会移入下一柱面。

磁盘的最小组成单位可以看成扇区,每个扇区的大小逻辑上看起来是512字节,但实际上底层的物理扇区是4096字节,可以通过如下方式去得到:

gqx@gqx-Lenovo-Product:~$ sudo fdisk -l
Disk /dev/sda: 931.5 GiB, 1000204886016 bytes, 1953525168 sectors
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 4096 bytes
I/O size (minimum/optimal): 4096 bytes / 4096 bytes

  由于扇区太小了,如果实际操作的时候按扇区为单位去读写数据的效率比较低,就比如10M的文件,磁头就需要读取(1024*10/512)20480次移动操作,所以一般都是用块(多个扇区)这个逻辑概念来实现具体的读写操作,一个块的大小通常是4096个字节:

gqx@gqx-Lenovo-Product:~$ sudo tune2fs -l /dev/sda4 | grep "Block size"
Block size: 4096

2. 文件系统的结构

  在UNIX系统里当我们将上述的磁盘结构上升一层到具体的文件层面时,就会将磁盘划分成很多个分区,每个分区对应一个文件系统,磁盘、分区和文件系统的结构如下:

  • 自举块:也称为引导块,分区中文件系统自身引导程序存放的地方。

  • 超级块:超级块在每个文件系统的根上,超级块描述和维护文件系统的状态。

  • ......

这里只关注柱面组里的i节点和数据块部分结构,其中数据块存放着文件的具体内容,如下图:

  每个i节点都有一个链接计数标记,表示指向该i节点的文件/目录的数量,只有当链接计数标记减少到0时,才会释放该文件所占用的数据块,可以从图中看到有两个目录项指向同一个i节点。

  i节点包含了文件的所有信息,包含文件的权限位信息,文件类型文件长度等(文件名保存在目录项中)。

  在不更换文件系统的情况下为一个文件重命名,该文件的实际位置并没有更改,只需在目录块中构造一个新的目录项指向原先的i节点,并将原来的目录项删除即可。

  可以举个栗子,假设我现在在一个空的工作目录中创建了一个目录(testdir),则现在这个目录中有三个子目录——("." , ".." 和 "testdir"),这三个目录以及testdir目录在柱面组里目录块和数据块的结构如下:

  图中,最右侧部分是工作目录的目录项,中间部分是testdir目录的目录项,通过这种组织结构可以从工作目录跳到testdir目录(i节点2549),也可以从testdir目录回到原先的工作目录(i节点1267)。

  i节点一般存放的是一些状态信息,所以影响到i节点的操作有很多,比如更改文件的访问权限,更改用户ID,更改链接数等,一些涉及到文件实际内容相关的操作都不会影响到节点。

3.文件I/O

  首先可以来看看Unix系统的体系结构如下图,所谓的内核,通常也是指操作系统(当然一般意义上的操作系统还包含了一些更加易于操作的交互功能等,如果忽略这些次要的东西,操作系统其实就是内核)。从图中可以看出应用程序既可以通过直接系统调用(内核对外提供的接口)来访问内核,也可以通过shell,公用函数库来达到一些目的。

  在I/O函数这里,根据程序所使用的方式不同——系统调用还是公用函数库调用,可以将I/O函数分成两类,带缓冲的I/O函数和不带缓冲的I/O函数。(严格意义上讲,传统的UNIX系统在内核中都会设有缓冲高速缓存或页高速缓存,当向文件写数据时,通常都会先把数据放入高速缓存中,然后在将其落盘,而此处的流缓存区是在公用函数库上做了又加了一层缓存区)

  • 无缓存IO操作数据流向路径:数据——内核缓存区——磁盘:不带缓冲的I/O函数——open,read,write,lseek和close。

  • 标准IO操作数据流向路径:数据——流缓存区——内核缓存区——磁盘:带缓冲的I/O函数——通常是以f为开头的操作函数等,比如fopen,fputs等。

输入输出

  文件描述符:内核用一个非负整数来标志一个特定进程正在访问的文件。

  每当运行一个程序的时候,所有shell都会为这个程序打开三个文件描述符——标准输入,标准输出,以及标准错误。

/* Standard file descriptors. POSIX 标准中 (unistd.h文件中定义) */
#define STDIN_FILENO 0 /* Standard input.STDIN_FILENO的值是0,表示标准输入文件描述符 */
#define STDOUT_FILENO 1 /* Standard output.STDOUT_FILENO的值是1,表示标准输出文件描述符*/
#define STDERR_FILENO 2 /* Standard error output. */

stdin和STD××_FILENO的区别

  • STDIN_FILENO是系统级API定义的变量,而stdin则是属于标准库处理的输入流

  • 数据类型不一样 stdin是FILE*类型的,STDIN_FILENO是int类型。

  • 使用stdin的函数主要有:fread、fwrite、fclose等,基本上都以f开头。而使用STDIN_FILENO的函数有:read、write、close等。

文件共享

  unix内核使用三种数据结构——进程表项,文件表项和v节点表项来表示打开的文件。

​   上图表示一个进程打开了标准文件输入(0)、一个标准输出打开(1)等。每个进程都会维护一个进程表项记录了①文件描述符的标志;②指向文件表项的指针。而文件表项则包含①文件状态标志(读、写、追加、阻塞等);②当前文件的偏移量;③v节点指针(v节点指针指向v节点表项)。v节点表项包含了对文件的操作函数指针,同时v节点还包含了指向i节点的指针,i节点的相关介绍由在前面已经说过。

现在如果两个独立的线程打开了同一个文件,这个时候,该文件的v节点表项会被共享,他们所共享的也就是v节点表项,如图,这样就可以维护多个不同的文件打开状态,同时又保证底层数据的空间开销最小,但也会带来一些新的问题,如何避免高并发下的发生文件操作的冲突问题。

4.文件的权限位

  这里不再简单的论述九个简单(r,w,x)权限位,而是对其余三个(和三个角色的最后一位执行权限共享位表示)做解释:

  1. setuid:设置使文件在执行阶段具有文件所有者的权限。比如一般用户想要更改自己的登入密码,而这个文件是存放在/etc/shadow下的,但是只有root才有权限去读写,这个时候就需要/usr/bin/passwd文件来帮我们完成。

    gqx@gqx-Lenovo-Product:~/桌面$ ll /usr/bin/passwd
    -rwsr-xr-x 1 root root 54256 5月 17 2017 /usr/bin/passwd*

    通过执行该命令,系统看到passwd命令文件的属性有小写s后,表示这个命令的属主权限被gqx用户获得,也就是gqx用户获得文件/etc/shadow的读写权限。

  2. setgid:该权限只对目录有效。目录被设置该位后,任何用户在此目录下创建的文件都具有和该目录所属的组相同的组。

  3. sticky bit(粘着位):该位可以理解为防删除位。 一个文件是否可以被某用户删除,主要取决于该文件所属的组是否对该用户具有写权限。如果没有写权限,则这个目录下的所有文件都不能被删除,同时也不能添加新的文件。 如果希望用户能够添加文件但同时不能删除文件,则可以对文件使用sticky bit位。设置该位后,就算用户对目录具有写权限也不能删除该文件。(比如ubuntu里的目录/tmp和/var/tmp, 除文件所有者外,超级用户也有权限删除,其他用户没有权限)

当前用户可以通过umask(既是C函数,也是shell的一个命令)来控制创建文件的权限:

gqx@gqx-Lenovo-Product:~/shellTest$ umask
0002
gqx@gqx-Lenovo-Product:~/shellTest$ touch a
-rw-rw-r-- 1 gqx gqx 0 7月 3 14:14 a
gqx@gqx-Lenovo-Product:~/shellTest$ umask 0022
gqx@gqx-Lenovo-Product:~/shellTest$ umask
0022
gqx@gqx-Lenovo-Product:~/shellTest$ touch b
gqx@gqx-Lenovo-Product:~/shellTest$ ls -l
-rw-rw-r-- 1 gqx gqx 0 7月 3 14:14 a
-rw-r--r-- 1 gqx gqx 0 7月 3 14:26 b
#创建新文件:读写权限为666-022=644

  参考资料:

    《UNIX环境高级编程》

APUE—UNIX文件系统的更多相关文章

  1. [apue] linux 文件系统那些事儿

    前言 说到 linux 的文件系统,好多人第一印象是 ext2/ext3/ext4 等具体的文件系统,本文不涉及这些,因为研究具体的文件系统难免会陷入细节,甚至拉大段的源码做分析,反而不能从宏观的角度 ...

  2. unix文件系统

    转自here 我一向坚持的原则,那就是任何东西的根本性的,本质上的原理以及背后的思想都是及其简单的,所谓的复杂性都是优化与策略化的扩展带来的,正如TCP一样,UNIX的文件系统也不例外!我们必须知道, ...

  3. Unix文件系统的主要特点是什么?

    1.  树型层次结构 2.  可安装拆卸的文件系统 3.  文件是无结构的字符流式文件 4.  Unix文件系统吧外部设备和文件目录作为文件处理

  4. 在Zookeeper中,znode是一个跟Unix文件系统路径相似的节点,可以往这个节点存储或获取数据

    在Zookeeper中,znode是一个跟Unix文件系统路径相似的节点,可以往这个节点存储或获取数据.如果在创建znode时Flag设置为EPHEMERAL,那么当创建这个znode的节点和Zook ...

  5. 《Unix&Linux大学教程》学习笔记6——Unix文件系统

    1:Unix文件类型——3种 普通文件(常规文件):文本文件(纯文本.脚本.源程序.配置文件.html等).二进制文件(多媒体文件.数据库等) 目录:用于组织文件 伪文件:不存储数据,目的是提供一种服 ...

  6. [APUE]UNIX进程的环境(下)

    一.共享库 共享库使得可执行文件中不再需要包含常用的库函数,而只需在所有进程都可存取的存储区中保存这种库例程的一个副本.程序第一次执行的时候或第一次调用某个库函数的时候,用动态链接方法将程序与共享库函 ...

  7. Unix 文件系统读写时权限校验

    文件系统中的所有文件都是在读出或写入时进行权限校验 一个问题,如果一个用户对一个普通文件有读写权限,在使用vim编辑时,管理员撤销掉此用户对此文件的写入权限 那么,这个普通用户还可以将修改写入文件吗?

  8. unix文件系统中的硬链接和软连接

    硬链接: 一般情况下,文件名和inode号码是"一一对应"关系,每个inode号码对应一个文件名.但是,Unix/Linux系统允许,多个文件名指向同一个inode号码. 这意味着 ...

  9. APUE(unix环境高级编程)第三版---first day---部署书中实例的运行环境(apue.h)

    操作环境:RHEL7.0 部署apue.h实例运行环境 1.下载头文件src.3e.tar.gz 2.解压 tar zxvf src.3e.tar.gz 3.创建普通用户(我仿照书上创建的sar用户) ...

随机推荐

  1. afnetwork使用

    Usage HTTP Request Operation Manager AFHTTPRequestOperationManager encapsulates the common patterns ...

  2. springBoot添加日志管理

    一. 近期自己的项目想要一个记录日志的功能,而springboot本身就内置了日志功能,然而想要输入想要的日志,并且输出到磁盘,然后按天归档,或者日志的切分什么的,自带的日志仅仅具有简单的功能,百度了 ...

  3. linux驱动学习笔记---实现中断下半部以及驱动编写规范(七)【转】

    转自:https://blog.csdn.net/weixin_42471952/article/details/81609141 版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协 ...

  4. java XML解析防止外部实体注入

    /** * 增加防止部实体注入逻辑 * <功能详细描述> * @param reader * @throws SAXException * @see [类.类#方法.类#成员] */ pu ...

  5. google v8

    https://github.com/tongbai168/v8 https://iwebing.lofter.com/tag/chromium  编译动态库 gyp mylib.gyp --dept ...

  6. MacBook安装WIN7开机黑屏的解决办法

    同事的一台Macbook,全盘用diskgenius格式化为MBR分区,支持BIOS启动,安装ghost版本的WIN7,开机黑屏,看了网上的帖子没有用,插入启动盘按option键选择从优盘启动,用di ...

  7. Java 函数式编程(Lambda表达式)与Stream API

    1 函数式编程 函数式编程(Functional Programming)是编程范式的一种.最常见的编程范式是命令式编程(Impera Programming),比如面向过程.面向对象编程都属于命令式 ...

  8. [LeetCode] 694. Number of Distinct Islands 不同岛屿的个数

    Given a non-empty 2D array grid of 0's and 1's, an island is a group of 1's (representing land) conn ...

  9. [LeetCode] 296. Best Meeting Point 最佳开会地点

    A group of two or more people wants to meet and minimize the total travel distance. You are given a ...

  10. OBS录制全屏游戏的方法(超好录屏)

    新版Windows设置 详见 https://github.com/obsproject/obs-studio/wiki/Laptop-Troubleshooting 新版的Windows 10: l ...