演示:文件的读写权限

1
2
3
4
5
6
7
8
9
10
[root@localhost 验证]# tree -N
.

0 directories, 0 files
[root@localhost 验证]# umask
0022
[root@localhost 验证]# echo "hell world" >newfile.txt
[root@localhost 验证]# ls -l newfile.txt
-rw-r--r--. 1 root root 11 6月 26 11:52 newfile.txt
[root@localhost 验证]#

创建新文件时,文件的权限受到两方面的影响,一个是umask值,另一个是重定向命令(>)的open调用时指定的文件权限。
没有修改权限的文件,依旧可以删除:

确定文件的权限

使用ls命令

有关选项-l和-d
例:ls -l可以查当前目录下所有文件和子目录的权限

1
2
3
4
5
6
7
8
9
[root@localhost Linux命令风格;文件系统]# ls -l
总用量 60
-rwxr-xr-x. 1 root root 71368 6月 23 15:04 dir
-rw-r--r--. 1 root root 536 6月 23 15:03 dir.c
-rwxr-xr-x. 1 root root 71136 6月 23 14:06 openfile
-rw-r--r--. 1 root root 291 6月 23 14:06 openfile.c
-rwxr-xr-x. 1 root root 71024 6月 18 22:19 useEnv
-rw-r--r--. 1 root root 239 6月 18 22:19 useEnvC
[root@localhost Linux命令风格;文件系统]#

ls -ld .列出当前目录自身的权限:

1
2
3
[root@localhost Linux命令风格;文件系统]# ls -ld
drwxr-xr-x. 2 root root 4096 6月 23 15:04 .
[root@localhost Linux命令风格;文件系统]#

chmod

chmod:修改权限(字母形式)

字母形式

1
chmod [ugoa][+-=][rwxst] 文件名表

u–user 文件主的权限
g–group 同组用户的权限
o–other 其他用户权限
a–all 所有上述三级权限
(t–Sticky, s–SUID)
例:

1
2
3
4
chmod u+rw * # 所有的文件的文件主都加上读权限和写权限
chmod go-rwx *.[ch] #对所有的.c或.h文件,同组用户和其他用户都减去读权限、写权限、执行权限
chmod a+x batch #对于batch文件的 所有用户(文件主,同组用户,其他用户)都加上执行权限
chmod u=rx try2 # 给try2文件的文件主的赋予读权限和执行权限

chmod:修改权限(数字形式)

数字形式(八进制数字)
例:

1
chmod 644  xyz1 xyz2
八进制: 6 4 4
二进制: 110 100 100
权限: rw- r– r–

注意: 只允许文件主和超级用户修改文件权限

umask命令:控制文件/目录的初始权限

功能:决定文件/目录的初始权限

  • 用vi新建文件
  • 用输出重定向创建文件
  • 创建新目录

这些新创建的文件或目录的权限就可以使用umask来指定。

umask是进程属性的一部分

  • umask是shell内部命令
  • umask是进程属性的一部分

命令

打印当前的umask值:

1
umask

将umask值设置为八进制的022

1
umask 022

进程umask属性的作用

掩码值的含义

例:
掩码值:022
二进制:000 010 010
取消新文件和新目录的组的w权限和其他用户的w权限

1
2
3
4
5
[root@localhost 文件和目录的权限]# umask
0022
[root@localhost 文件和目录的权限]# ls -ld
drwxr-xr-x. 2 root root 4096 6月 24 11:08 .
[root@localhost 文件和目录的权限]#

禁止组的所有权限和其他用户的所有权限(比特为1的地方的权限被屏蔽掉)

1
umask 077

自动执行批处理文件

umask对当前shell进程有效,关闭当前shell后,umask属性将会丢失。
所以,如果需要的话,一般将umask命令放到shell自动执行批处理文件中
例如放到bash用户偏好文件中($HOME/.bash_profile),

示例

查看当前shell进程的umask值,查看当前目录的权限:

1
2
3
4
5
6
[root@localhost 文件和目录的权限]# ls
[root@localhost 文件和目录的权限]# umask
0022
[root@localhost 文件和目录的权限]# ls -ld
drwxr-xr-x. 2 root root 4096 6月 24 11:08 .
[root@localhost 文件和目录的权限]#

修改当前shell进程的umask值为077,已经存在的目录不会受影响:

1
2
3
4
[root@localhost 文件和目录的权限]# umask 077
[root@localhost 文件和目录的权限]# ls -ld
drwxr-xr-x. 2 root root 4096 6月 24 11:08 .
[root@localhost 文件和目录的权限]#

新创建的目录或文件的权限将受影响:

1
2
3
4
5
6
7
[root@localhost 文件和目录的权限]# mkdir sonDir
[root@localhost 文件和目录的权限]# touch test.c
[root@localhost 文件和目录的权限]# ls -l
总用量 4
drwx------. 2 root root 4096 6月 24 11:57 sonDir
-rw-------. 1 root root 0 6月 24 11:57 test.c
[root@localhost 文件和目录的权限]#

修改shell进程的umask值后,对整个shell进程创建的新目录或文件都有影响:

1
2
3
4
5
6
7
8
9
[root@localhost 文件和目录的权限]# cd ..
[root@localhost Linux_Test]# ls -ld
drwxr-xr-x. 22 root root 4096 6月 24 12:02 .
[root@localhost Linux_Test]# mkdir umask
[root@localhost Linux_Test]# touch umask.c
[root@localhost Linux_Test]# ls -l |grep 'umask*'
drwx------. 2 root root 4096 6月 24 12:02 umask
-rw-------. 1 root root 0 6月 24 12:02 umask.c
[root@localhost Linux_Test]#

umask命令只对当前shell有效,重新打开一个shell,umask为默认值022:

1
2
3
4
5
6
7
8
9
[root@localhost 文件和目录的权限]# ls -ld
drwxr-xr-x. 3 root root 4096 6月 24 11:57 .
[root@localhost 文件和目录的权限]# ls -l
总用量 4
drwx------. 2 root root 4096 6月 24 11:57 sonDir
-rw-------. 1 root root 0 6月 24 11:57 test.c
[root@localhost 文件和目录的权限]# umask
0022
[root@localhost 文件和目录的权限]#

另一个shell的umask命令不会影响当前shell新创建的目录或文件的权限:

1
2
3
4
5
6
7
8
9
[root@localhost 文件和目录的权限]# touch newShellnewFile.c
[root@localhost 文件和目录的权限]# mkdir NewShellNewDir
[root@localhost 文件和目录的权限]# ls -l
总用量 8
drwxr-xr-x. 2 root root 4096 6月 24 12:06 NewShellNewDir
-rw-r--r--. 1 root root 0 6月 24 12:05 newShellnewFile.c
drwx------. 2 root root 4096 6月 24 11:57 sonDir
-rw-------. 1 root root 0 6月 24 11:57 test.c
[root@localhost 文件和目录的权限]#

系统调用umask

功能

修改进程自身的umask属性值

初创文件的权限

  • 受open的规定值和进程自身属性umask值双重影响
  • 已存在的文件的权限,不受open/umask的影响

例:当umask为077时,用C程序

1
fd=open(filename,O_CREAT|O_WRONLY,0666);

open的权限为0666,经过umask屏蔽掉077后文件的实际权限为0600

系统调用umask

1
int umask(int mask); 
  • mask为指定的新umask值,返回值为原先的umask值
  • 读出进程umask属性而不改变它,需调umask两次

目录的读写权限

读权限

  • 若无读权限,那么“目录表”文件不许读,ls会失败

写权限

  • 若无写权限,那么“目录表”文件不许写
  • 创建文件,删除文件,文件改名会修改目录文件
  • 修改文件不需要修改目录文件,需要修改i节点
  • 目录无写权限不是指目录下所有文件禁止写

目录的x权限

执行权限

  • 有执行权限意味着分析路径名过程中可检索该目录
  • cat /a/b/c
  • 要求/,/目录下的a目录,/目录下的a目录下的b目录,这三目录有x权限,c文件有读权限;否则,命令执行失败
  • cd ../st8要求当前目录,..st8必须有x权限

STICKY权限(粘着位)

STICKY文件

  • 早期Unix具有sticky属性的可执行文件尽量常驻内存或交换区以提高效率
  • 现代Linux对访问过的文件自动缓冲在内存,文件sticky属性被忽略

STICKY目录

  • 问题:对于公共目录,用户user1和user2没有写权限,就不可以在这个目录下创建新文件;若有写权限,用户user1的文件就算是“只读文件”也可以被user2删除
  • STICKY属性用于解决这个问题:目录有写权限并且带STICKY属性,此目录下的文件仅文件主可以删除,其他用户删除操作会失败
  • 例如:/tmp目录, ls -ld /tmp,输出的第一列最后字符为t:
drwxrwxrwt 13 root root 20480  Mar 22 06:11 /tmp

权限验证的顺序

每个文件都有文件主和组的属性(文件节点中)
每个进程也有进程主和组的属性(进程PCB中)
这四个属性都是整数,uid和gid的编号与名字对应关系见/etc下passwd和group文件

文件主与进程主相同

直接使用文件主的权限,不再查看组和其他用户的权限

文件主与进程主不同,但文件主与进程主同组

只使用组权限,不使用关于其他用户的权限

文件主与进程主不同,文件主与进程主又不同组

使用文件关于其他用户的权限。

注意:超级用户root不受权限的限制

例:权限---r--rw-,文件主不可读但同组用户可读,即使文件主是该组用户之一也不行

文件的权限

用于控制进程对系统中文件和目录的访问

权限的三个级别

  • 文件主,同组用户,其他用户
  • 每个文件有唯一的属主

普通文件的权限

  • 读、写、可执行
  • 不可写文件也可能会被删除

两类可执行文件

程序文件(可执行文件)

  • 二进制的CPU指令集合,满足操作系统规定的格式才可以被加载运行

脚本文件:文本文件

  • 默认的解释程序为/bin/sh
  • 可以在文件的第一行自行指定解释程序(必须是第一行,#!必须是这个文件首先出现的两个字符),例如:
    1
    2
    #!/bin/bash 
    #!/usr/bin/bc
  • 解释程序也可以是用户自己编写的应用程序
  • 脚本程序运行时,实际上是由解释程序创建了一个进程

#!是一个约定的标记,它告诉系统这个脚本需要什么解释器来执行,即使用哪一种Shell。

CentOS安装C语言帮助手册

CentOS系统有可能默认没有安装C语言帮助手册,man一个函数时会找不到帮助文件,用下面的命令安装:

1
yum install man-pages.noarch

centos没有c语言的帮助手册

1
2
3
[root@localhost Linux命令风格;文件系统]# man malloc
没有 malloc 的手册页条目
[root@localhost Linux命令风格;文件系统]#

安装.noarch版本的man-pages

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
[root@localhost Linux命令风格;文件系统]# yum install man-pages.noarch
已加载插件:fastestmirror
Determining fastest mirrors
epel/aarch64/metalink | 8.8 kB 00:00:00
* epel: mirrors.bfsu.edu.cn
base | 3.6 kB 00:00:00
centos-7-aarch64 | 3.6 kB 00:00:00
extras | 2.9 kB 00:00:00
nodesource | 2.5 kB 00:00:00
updates | 2.9 kB 00:00:00
(1/2): extras/7/aarch64/primary_db | 245 kB 00:00:01
(2/2): updates/7/aarch64/primary_db | 1.2 MB 00:00:05
正在解决依赖关系
--> 正在检查事务
---> 软件包 man-pages.noarch.0.3.53-5.el7 将被 安装
--> 解决依赖关系完成

依赖关系解决

============================================================================================================================================================
Package 架构 版本 源 大小
============================================================================================================================================================
正在安装:
man-pages noarch 3.53-5.el7 base 5.0 M

事务概要
============================================================================================================================================================
安装 1 软件包

总下载量:5.0 M
安装大小:4.6 M
Is this ok [y/d/N]: y
Downloading packages:
man-pages-3.53-5.el7.noarch.rpm | 5.0 MB 00:00:21
Running transaction check
Running transaction test
Transaction test succeeded
Running transaction
正在安装 : man-pages-3.53-5.el7.noarch 1/1
验证中 : man-pages-3.53-5.el7.noarch 1/1

已安装:
man-pages.noarch 0:3.53-5.el7

完毕!
[root@localhost Linux命令风格;文件系统]#

验证

man malloc

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
MALLOC(3)                                                       Linux Programmer's Manual                                                      MALLOC(3)

NAME
malloc, free, calloc, realloc - allocate and free dynamic memory

SYNOPSIS
#include <stdlib.h>

void *malloc(size_t size);
void free(void *ptr);
void *calloc(size_t nmemb, size_t size);
void *realloc(void *ptr, size_t size);

DESCRIPTION
The malloc() function allocates size bytes and returns a pointer to the allocated memory. The memory is not initialized. If size is 0, then
malloc() returns either NULL, or a unique pointer value that can later be successfully passed to free().
......

man 3 printf

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
PRINTF(3)                                                       Linux Programmer's Manual                                                      PRINTF(3)

NAME
printf, fprintf, sprintf, snprintf, vprintf, vfprintf, vsprintf, vsnprintf - formatted output conversion

SYNOPSIS
#include <stdio.h>

int printf(const char *format, ...);
int fprintf(FILE *stream, const char *format, ...);
int sprintf(char *str, const char *format, ...);
int snprintf(char *str, size_t size, const char *format, ...);

#include <stdarg.h>

int vprintf(const char *format, va_list ap);
int vfprintf(FILE *stream, const char *format, va_list ap);
int vsprintf(char *str, const char *format, va_list ap);
int vsnprintf(char *str, size_t size, const char *format, va_list ap);

Feature Test Macro Requirements for glibc (see feature_test_macros(7)):

snprintf(), vsnprintf():
_BSD_SOURCE || _XOPEN_SOURCE >= 500 || _ISOC99_SOURCE || _POSIX_C_SOURCE >= 200112L;
or cc -std=c99

DESCRIPTION
......

参考资料

https://blog.csdn.net/yasi_xi/article/details/8658333
https://blog.csdn.net/caiyaodeng/article/details/45291905

统调用stat/fstat:从i节点获得文件的状态信息

从i节点获得文件的状态信息

  • stat得到指定路径名的文件的i节点
  • fstat得到已打开文件的i节点

stat和fstat将数据放入调用者提供的stat结构中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
int stat(const char *pathname, struct stat *buf);
int fstat(int fd, struct stat *buf);
struct stat {
dev_t st_dev; /* 存储该文件的块设备的设备号ID */
ino_t st_ino; /* inode号 */
mode_t st_mode; /* 访问权限及文件类型 */
nlink_t st_nlink; /* link数 */
uid_t st_uid; /* 文件主ID */
gid_t st_gid; /* 组ID */
dev_t st_rdev; /* device ID (if special file) */
off_t st_size; /* 文件大小(字节数)*/
blksize_t st_blksize; /* blocksize for filesystem I/O */
blkcnt_t st_blocks; /* 分配的512字节尺寸块个数 */
struct timespec st_atim; /* access时间 */
struct timespec st_mtim; /* modification时间 */
struct timespec st_ctim; /* change时间 */
};

结构体stat

 st_dev:存储该文件的块设备的设备号,包括主设备号与次设备号
例如stat命令显示文件Device: 821h/2081d
十六进制0821,主设备号8(高字节),次设备号33(低字节), /dev/sdc1

1
2
ls -l /dev | grep '^b.* 8, *33'
brw-rw---- 1 root disk 8, 33 Nov 18 10:40 sdc1

st_mode域:16比特

文件的基本存取权限和SUID/SGID权限(11比特)以及文件的类型(若干比特)
文件类型判st_mode & S_IFMT
S_IFREG 普通磁盘文件
S_IFDIR 目录文件
S_IFCHR 字符设备文件
S_IFIFO 管道文件
S_IFLNK 符号连接文件

st_size与st_blocks

程序可以通过st_size获取文件大小。
一般情况:st_size ≤ st_blocks * 512
稀疏文件:st_size > st_blocks * 512

st_ctim,st_atim,st_mtim域

Linux中存储这三个时间的精度为纳秒
“a访问”:读,执行(有些系统为了效率做懒惰处理,不更新,但不早于m时间)
“m修改”:文件内容修改。写文件
“c改变”:i节点信息变化。写文件,修改权限/link数/文件主等(m变,c也变)

目录访问

早期的UNIX象普通磁盘文件那样open()打开目录read()读取
现在的系统不再这样操作,而是直接使用封装好的库函数

目录访问的一组库函数

1
2
3
4
#include <dirent.h>
DIR *opendir(char *dirname);
struct dirent *readdir(DIR *dir);
int closedir(DIR *dir);

opendir打开目录得到句柄(NULL表示失败)
readdir获取一个目录项

  • 返回值指针指向的dirent结构体(返回NULL表示已经读到目录尾)
  • dirent结构体:记录i节点号和文件名(d_ino和d_name成员)
    访问结束:用closedir关闭不再使用的目录句柄。

目录访问程序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include <stdio.h>
#include <dirent.h>
#include <errno.h>
#include <stdlib.h>

int main(int argc, char *argv[])
{
DIR *dir;
struct dirent *entry;
if (argc != 2) {
fprintf(stderr, "Usage : %s <dirname>\n", argv[0]);
exit(1);
}
if ((dir = opendir(argv[1])) == NULL) {
printf("Open directory \"%s\": %s (ERROR %d)\n", argv[1], strerror(errno), errno);
exit(1);
}
while ((entry = readdir(dir)) != NULL)
printf("%d %s\n", entry->d_ino, entry->d_name);
closedir(dir);
}

运行效果:

1
2
3
4
5
6
7
8
9
10
[root@localhost Linux命令风格;文件系统]# ./dir .
524351 .
524325 ..
524413 useEnv
524326 openfile
524446 useEnvC
524336 openfile.c
524346 dir
524353 dir.c
[root@localhost Linux命令风格;文件系统]#

利用目录访问和stat调用来 编程构造自己的工具

  • 目录访问:可以穷举一个目录中的所有项目,可得文件名及节点号
  • stat调用:可以根据文件名获得文件i节点中的状态信息

由此,可以编程构造诸如ls, rm, find等类似的工具,并理解系统命令是如何实现的。

系统调用(System call)

  • 系统调用以C语言函数调用的方式提供
  • 操作系统内核提供的编程界面
    • 应用程序(ap)和操作系统(kernel)进行交互的唯一手段
    • 例如:文件操作的open,read,write,close
  • 种类
    • 早期UNIX有50多个,后来扩充到120个,Linux有300个左右

系统调用与库函数在执行方式上的区别

例如:获取进程ID的getpid()与字符串拷贝函数strcpy()
CPU的INT指令(软中断)与CALL指令(子程序调用)

库函数对系统调用的封装(API)

目的:执行效率更高或者调用界面更方便。例如:
库函数printf对系统调用write的封装
库函数malloc/free对系统调用sbrk的封装

可移植性

系统调用和相关API函数以及库函数的名称、参数排列顺序、参数类型,返回值的类型,以及实现的功能,都属于类似POSIX标准规范的内容,便于不同Unix系统之间的移植。

系统调用函数的返回值

返回值

一般返回一个整数值

  • 返回值大于或等于零:成功
  • 返回值为-1:失败

整型变量errno

标准库为errno保留存储空间,系统调用失败后填写错误代码,记录失败原因
#include <errno.h>之后,就可以直接使用变量errno.

errno.h头文件定义了许多有E前缀的宏,例如

1
EACCESS,EIO,ENOMEM,EINTR

相关系统调用的手册页中有出错说明
在man命令给出的手册页中有ERRORS一节介绍出错原因,如man recv

库函数strerror与printf的格式符%m 打印错误返回值

strerror

1
char *strerror(int errno);

errno是个整数,便于程序识别错误原因,不便于操作员理解失败原因。
库函数strerror将数字形式的错误代码转换成一个可阅读的字符串

printf的%m

printf类函数格式字符串中的%m会被替换成上次系统调用失败的错误代码对应的消息(message)

openfile.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <fcntl.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
int main(char argc, char *argv[])
{
int fd;
fd = open(argv[1], O_WRONLY);
if (fd == -1) {
printf("ERROR %d: %m\n", errno);
printf("ERROR [%s]\n", strerror(errno));
}
//......
}

创建openfile.c并编编译成可执行文件openfile:

1
2
3
4
5
6
7
[root@localhost Linux命令风格;文件系统]# vim openfile.c
[root@localhost Linux命令风格;文件系统]# ls
openfile.c useEnv useEnvC
[root@localhost Linux命令风格;文件系统]# gcc openfile.c -o openfile
[root@localhost Linux命令风格;文件系统]# ls
openfile openfile.c useEnv useEnvC
[root@localhost Linux命令风格;文件系统]#

输入一个存在的文件,运行效果如下:

1
2
[root@localhost Linux命令风格;文件系统]# ./openfile useEnvC 
[root@localhost Linux命令风格;文件系统]#

可以看到没有错误输出。

1
故意输入一个不存在的文件,运行效果如下:

[root@localhost Linux命令风格;文件系统]# ./openfile useEnv.c
ERROR 2: No such file or directory
ERROR [No such file or directory]
[root@localhost Linux命令风格;文件系统]#


符号链接

符号链接也叫软链接

  • 用特殊文件“符号链接文件”来实现
  • 文件中仅包括了一个路径名
  • 命令ln -s和ls -l
1
2
3
ln -s users_on sym.link
ls -l sym.link
lrwxrwxrwx 1 guest other 8 Jul 26 16:57 sym.link->users_on
  • 类型为l,大小为8字节,文件中只存放users_on字符串
  • 文件的最后一次写时间以后不再变化
  • 一旦建立了符号链接,删除操作删除的是符号链接文件,其它所有操作都将访问符号链接所引用的文件

符号链接的实现

符号链接中的相对路径

若符号链接包含绝对路径名,引用绝对路径名
若符号链接包含相对路径名,是相对于符号链接文件的位置(不是相对于调用进程的当前工作目录)

设当前目录(bash进程的当前目录)为d

1
ln -s  d1/dlb d1/dx

在d1下新建文件dx
访问d1/dx实际访问d1/d1/d1b

硬链接与符号链接的比较

硬链接

  • 在数据结构层次上实现
  • 只适用于文件,不适用于目录
  • 不同文件系统之间也不行
  • 硬链接能够完成的功能软链接可以做到

符号链接

  • 在算法软件上实现
  • 硬链接能够完成的功能软链接都可以做到
  • 适用于目录,也适用于不同的文件系统
  • 同硬链接相比要占用操作系统内核的一部分开销
  • 循环式符号链接,以及处理方法(解析路径时设置符号链接解析计数器)

Windows符号链接 软链接

https://blog.csdn.net/x534119219/article/details/79111936

mklink帮助文档

1
2
3
4
5
6
7
8
9
10
11
12
13
14
C:\Users\my>mklink /?
创建符号链接。

MKLINK [[/D] | [/H] | [/J]] Link Target

/D 创建目录符号链接。默认为文件
符号链接。
/H 创建硬链接而非符号链接。
/J 创建目录联接。
Link 指定新的符号链接名称。
Target 指定新链接引用的路径
(相对或绝对)。

C:\Users\my>

示例 给目录创建符号链接

D:\网络共享目录下,给G:\dev2\idea_workspace\MyJavaTools\runable目录创建一个同名的符号链接:

1
2
3
4
C:\Users\my>mklink /j D:\网络共享\runable G:\dev2\idea_workspace\MyJavaTools\runable
为 D:\网络共享\runable <<===>> G:\dev2\idea_workspace\MyJavaTools\runable 创建的联接

C:\Users\my>

硬连接

  • 目录表由目录项构成,目录项是一个“文件名-i节点号”对
  • 根据文件系统的存储结构,可以在同一目录或者不同目录中的两个目录项,有相同的i节点号
  • 每个目录项指定的“文件名-i节点号”映射关系,叫做1个硬连接
  • 硬连接数目(link数):同一i节点被目录项引用的次数

ln: 普通文件的硬连接

创建一个文件的硬链接:

1
2
3
4
5
6
[root@localhost Linux命令风格;文件系统]# ls
useEnv useEnv.c
[root@localhost Linux命令风格;文件系统]# ln useEnv.c useEnvC
[root@localhost Linux命令风格;文件系统]# ls
useEnv useEnv.c useEnvC
[root@localhost Linux命令风格;文件系统]#

查看文件以及文件的硬链接的详细信息:

1
2
3
4
5
6
[root@localhost Linux命令风格;文件系统]# ls -l
总用量 24
-rwxr-xr-x. 1 root root 71024 6月 18 22:19 useEnv
-rw-r--r--. 2 root root 239 6月 18 22:19 useEnv.c
-rw-r--r--. 2 root root 239 6月 18 22:19 useEnvC
[root@localhost Linux命令风格;文件系统]#

可以发现useEnv.c和useEnvC的前几项都完全相同。而useEnv.c和useEnvC的link数都是2.
查看文件的i节点:

1
2
3
[root@localhost Linux命令风格;文件系统]# ls -i
524413 useEnv 524446 useEnv.c 524446 useEnvC
[root@localhost Linux命令风格;文件系统]#

可以发现文件useEnv.c和useEnvC的i节点都相同。
useEnv.c与useEnvC同时存在时,地位完全平等,硬链接创建完毕后,就无法知道哪个文件是先创建的,哪个文件是后创建的。
删除useEnv.c文件,

1
2
3
4
5
6
7
[root@localhost Linux命令风格;文件系统]# rm useEnv.c 
rm:是否删除普通文件 "useEnv.c"?y
[root@localhost Linux命令风格;文件系统]# ls -l
总用量 20
-rwxr-xr-x. 1 root root 71024 6月 18 22:19 useEnv
-rw-r--r--. 1 root root 239 6月 18 22:19 useEnvC
[root@localhost Linux命令风格;文件系统]#

可以发现则useEnvC仍存在但link数减1。
只有一个文件的link数减到0时,该文件的磁盘空间才会被清理掉。

硬连接,只限于同一文件系统中普通文件

目录表的硬连接

不允许对目录用ln命令建立硬连接
一般来说,目录的link数=直属子目录数+2

目录表的硬连接示意图:

硬链接的作用

可以对同一个文件起不同的文件名

一个文件位于另一个个文件系统中,不同的文件系统的i节点不能通用。
也就是硬链接不能跨越文件系统。

文件系统的结构

把整个逻辑设备以块(扇区) 为单位为划分,编号为0,1,2,…。
(每块512字节或其他更大$2^n$字节大小)
图片

引导块(0号块)

用于启动系统,只有根文件系统的引导块有效

专用块(1号块)

也叫管理块,或者超级块

  • 存放文件系统的管理信息。如:文件系统的大小,i节点区的大小,空闲空间大小,空闲块链表的头等等
  • mkfs命令时初始化,df命令读出部分信息,df -i和df
1
2
3
4
5
6
7
8
9
10
11
12
13
[root@localhost Linux命令风格;文件系统]# df -h
文件系统 容量 已用 可用 已用% 挂载点
/dev/block/bootdevice/by-name/userdata 55G 24G 31G 44% /
tmpfs 1.8G 368K 1.8G 1% /dev
tmpfs 1.8G 0 1.8G 0% /dev/shm
/data/media 55G 24G 31G 44% /mnt/files
[root@localhost Linux命令风格;文件系统]# df -hi
文件系统 Inode 已用(I) 可用(I) 已用(I)% 挂载点
/dev/block/bootdevice/by-name/userdata 3.5M 153K 3.4M 5% /
tmpfs 445K 910 445K 1% /dev
tmpfs 450K 1 450K 1% /dev/shm
/data/media 3.5M 153K 3.4M 5% /mnt/files
[root@localhost Linux命令风格;文件系统]#

i节点区

i节点区:i节点(index node,简记为i-node)

  • i节点区由若干块构成,在mkfs命令创建文件系统时确定
  • 每块可容若干个i节点,每个i节点的大小是固定的(比如64字节)
  • i节点从0开始编号,根据编号可以索引到磁盘块
  • 每个文件都对应一个i节点,i节点中的信息包括:
    • 指向文件存储区数据块的一些索引(index)指针(组成文件的逻辑块与硬盘的物理块之间的映射)
    • 文件类型,属主,组,权限,link数,大小,时戳(i节点内不含文件名)

文件存储区

用于存放文件数据的区域,包括目录表

目录的存储结构

Linux目录结构是树形带交叉勾连的目录结构

目录表

  • 每个目录表也作为一个文件来管理,存于“文件存储区”中,有其自己的i节点和数据存储块
  • 目录表由若干个“目录项”构成,目录项只含两部分信息:
    • 文件名
    • i节点号
  • 用ls命令列出的目录大小是目录表文件本身的长度

目录存储结构示意图

图片

目录表和i节点两级结构

主要目的:分开存放的主要目的是为了提高目录检索效率

假定的环境

  • 存储文件名使用定长14字节,索引信息需要64字节,每磁盘块512字节
  • 当前目录下有100个文件,需要访问的文件的文件名mydata.bin存放在目录表的最末尾处

方案一:一级结构

  • 文件名和索引信息存放在一起,放在目录表中
  • 每个目录项78字节,每块可容纳512÷78=6个目录项
  • 读入目录直到第17块才找到mydata.bin以及索引信息,根据索引信息访问文件存储区的数据块

方案二:两级结构

  • 文件名和索引信息分开,索引信息存放在i节点中,目录表中仅记录文件名和i节点的2字节编号
  • 每个目录项16字节,一个磁盘块含512÷16=32个目录项
  • 读入4块就检索到名字mydata.bin的i节点号
  • 根据i节点号访问对应的磁盘块,读入i节点中的索引信息
  • 总共磁盘操作5块,就可以根据名字找到文件的索引信息

两种方案的比较

  • 后者需要更少的磁盘访问次数
  • 文件系统采用这样的存储结构,完全可以在同一目录或者不同目录中的两个目录项,有相同的i节点号

命令stat:读取i节点信息

示例:

1
2
3
4
5
6
7
8
9
10
[root@localhost Linux命令风格;文件系统]# stat useEnv.c 
文件:"useEnv.c"
大小:239 块:8 IO 块:4096 普通文件
设备:10315h/66325d Inode:524446 硬链接:1
权限:(0644/-rw-r--r--) Uid:( 0/ root) Gid:( 0/ root)
最近访问:2021-06-18 22:19:04.164832314 +0800
最近更改:2021-06-18 22:19:04.164832314 +0800
最近改动:2021-06-18 22:19:04.164832314 +0800
创建时间:-
[root@localhost Linux命令风格;文件系统]#