Linux环境编程笔记--文件与目录

1、ext2文件系统

示意图:Linux环境编程笔记--文件与目录_第1张图片Block(块):
物理磁盘是由一个个的区(所谓的扇区)连接成一片,每个区是 512 字节。为了能有组织,有计划的使用磁盘,设计者把连续若干个(比如2个,4个,8个)区连成一组,抽象出 block 的概念。如果使用 2 个区为一组,那一个 block 的大小就是 1KB。如今,磁盘也不在是什么昂贵资源,动辄就是几百G大小,通常在设计 block 的时候都使用 8 区连成一组,即一个 block 是 4KB。

Boot Block(引导区):
无论如何,Boot Block 都占用 1KB,它并不以你划分的 block 大小来改变自己。即使你划分的 block 大小是 4KB,Boot Block 也只占用 1KB 大小。这个块,是专门为启动CPU的时候保留的(我没说是启动操作系统)。
在你摁下开机键的那一刻,CPU 一上电,硬件会主动把 Boot Block 复制到内存的0x7c00这个位置,然后CPU 就从这个位置开始执行,后面的事情,就是一步一步的把操作系统引导起来。

inode table(inode表)
inode表中存放了inode节点,inode节点记录了文件存放位置、文件权限、最近打开时间等信息。但是并没有存储文件名,文件名则是被放在了称为目录项的结构体中
Linux环境编程笔记--文件与目录_第2张图片

inode bitmap(inode位图)和block bitmap(块位图):
位图中的每一位用来表示一个位置,为1则表示被占用,为0表示没有被占用。inode位图用来记录inode表的占用情况,block位图记录block的占用情况。
Linux环境编程笔记--文件与目录_第3张图片

Group Descriptor Table(组描述符表)
一个组描述符表,它不仅存储当前组的信息,也会保存其它组的信息。这是一种备份思想。组描述符表中存储的组描述符。
组描述符中保存了 inode 位图和 block 位图在哪个 block 上。除此还有 inode 表的位置等等。
Linux环境编程笔记--文件与目录_第4张图片

Super Block(超级块):
记录一些全局信息,如:inode节点个数、block个数、第一个数据块的位置等。
Linux环境编程笔记--文件与目录_第5张图片

2、stat函数相关

2.1 stat()函数

linux 中,可以使用 stat 函数来获取文件相关的信息,就比如说文件的大小,文件的类型等等。stat 函数将获取到的结果保存到一个名为 struct stat 的结构体中。它的样子如下:

 struct stat {
    dev_t     st_dev;         /* 包含这个文件的设备 ID */
    ino_t     st_ino;         /* inode 编号 */
    mode_t    st_mode;        /* 访问权限 */
    nlink_t   st_nlink;       /* 硬链接数量 */
    uid_t     st_uid;         /* 用户ID */
    gid_t     st_gid;         /* 组ID */
    dev_t     st_rdev;        /* 设备ID */
    off_t     st_size;        /* 文件占用的字节数 */
    blksize_t st_blksize;     /* 文件系统块大小 */
    blkcnt_t  st_blocks;      /* 文件占用了几个512字节 */
    time_t    st_atime;       /* 最后访问时间 */
    time_t    st_mtime;       /* 最后更改时间 */
    time_t    st_ctime;       /* 最后状态更改时间 */
};

2.2 stat结构体 st_mode字段

Linux环境编程笔记--文件与目录_第6张图片
15-12 位保存文件类型
11-9 位保存执行文件时设置的信息
8-0 位保存文件访问权限

黏着位:
黏着位对目录文件时有用的,当此目录黏着为1时,虽然多用户对此目录有读写权限,可以在此目下创建文件,但是此目录下文件只能被所有者删除或重命名。

3、chomd()、chown()和utime()函数:

3.1chomd()函数

chmod 函数做的是与 stat 获取 i_mode 值相反的操作,它是把值存储到 i_mode 字段里。不过有一点需要注意的是,它只能改变 i_mode 的低 12 位,因为高 4 位保存的是文件类型,就算你改了也不会起作用。
chomd也可以命令形式对文件进行修改,将参数放到chomd后即可。

/ 输入:文件路径和 mode。这里的 mode_t 就是一个整型。
// 返回:0 表示成功,-1 失败
int chmod(const char *path, mode_t mode);

3.2chown()函数

chown 命令只能工作在 root 权限下,主要用来修改文件的所有者和所属组。

$ sudo chown root:jack test.txt  // 运行该命令必须具备 root 权限

上面命令的作用是将test.txt文件所有者修改为root,所属组修改为jack。
也可以按照函数体在程序中以函数的形式使用chown()。

/ pathname: 要更改的文件名
// owner: 拥有者 uid
// gropu: 所属组 gid
// 返回 0 成功,-1 失败
int chown(const char *pathname, uid_t owner, gid_t group);

3.3utime()函数

作用:修改文件的最近访问时间(atime)和最近修改时间(mtime).

*
struct utimbuf {
    time_t actime;       // access time
    time_t modtime;      // modification time
};
*/
int utime(const char *filename, const struct utimbuf *times);

4、文件截断truncate 函数

所谓的文件截断 ,就是把文件尾巴砍掉,留下固定长度的文件。truncate函数就是用来实现这个功能的:

int truncate(const char *path, off_t length);

参数length可以比当前文件长度小,那么多出的部分就会被砍掉,形成length长度的文件。
length也可以比当前文件长度大,这种情况下,就会在文件后面补0。使用stat命令获取到的文件长度为length,但是占用的block*512(或者1024,在Linux系统上,这取决于是否设置了POSIXLY_ CORRECT环境变量)却小于length,这就是所谓的文件空洞。这是由于read操作对于没有写过的字节读取到的字节长度为0,当使用实用程序(如cat)复制这个文件时,这些所有的空洞都会被0填满。

*5、硬链接和软链接

首先,硬链接可以看作是文件的别名,而软链接也叫符号链接,是文件的一种。
link系列函数创建、删除、读取软链接和硬链接

函数 功能
link 创建硬链接
symlink 创建软链接(符号链接)
unlink 删除硬链接或者软链接(不能删除目录,如果想删除目录,可以使用 rmdir 函数或者 ANSI C 中的 remove 函数。)
readlink 读取软链接文件自身内容(目标文件路径)(不是所指向的文件内容)

硬链接

ln命令创建硬链接
每个文件都有一个索引,也就是inode,里面存储了文件的权限、访问时间、文件位置等等,但是一个inode可能对应多个link,也就是inode中的i_links_count可以大于1.这是种现象就是硬链接。创建一个文件的多个inode对应,这些对应都指向同一个inode(在目录项种的多个dir_entry的inode节点编号相同),相当于给同一个文件起了一些别名,当不小心删除了一个文件名(实际是删除了一个链接),还可以通过别名找到这个文件。只有当所有的硬链接都被删除时,即i_links_count==0时,才会释放文件所占有的空间。

软链接

ln -s创建软链接
软链接也叫符号链接,是文件的一种。既然是一个单独的文件,那么它也有自己的inode节点,与指向的文件不同。它内容是目标文件的路径,所以可以通过符号链接的内容找到目标文件,这类似于Windows中常用的快捷方式。

区别

1、硬链接只能在本文件系统中起作用,不能跨文件系统,软链接可以。
2、普通用户不能创建目录的硬链接(因为这有可能会造成死循环,可以参照下面依据我自己的理解画的“丑图”,如有错误,欢迎指正),可以创建任意文件的软链接(软链接不会造成循环,因为系统在遍历目录的过程中可以在inode中看到文件属性,遇到的符号链接超过8个就会自动停止)。
3、解除一个文件的链接时,可以通过其他的硬链接找到文件,但是通过这个路径创建的软链接会失效。
4、文件属性和大小不同,硬链接与原文件一样。软链接的文件属性即符号链接,根据内容有自己的大小。
5、硬链接与原文件对应同一个inode,所以它们是同一个文件,而软链接与原文件是不同的文件。
Linux环境编程笔记--文件与目录_第7张图片

6、目录相关

当前工作目录:
当前工作目录是针对进程而言的,它是进程的属性。
chdir函数
与命令cd类似,进行目录切换。

getcwd函数
与pwd(print working dir)命令类似,获取当前工作目录。

如果在程序中写了这两个函数,例如使用chdir进入子目录,然后利用getcwd函数获取当前工作目录并打印。在shell父目录中执行这个程序,会输出chdir进入的子目录。但是使用pwd命令,会输出父目录。这是为什么呢?因为当前工作目录是针对进程而言的。getcwd获取的是你写的程序的进程的当前目录,pwd打印的是shell进程的工作目录,当然就不一样了!

mkdir:命令和函数均用来创建空目录,但是mkdir函数需要指定目录的权限位。

int mkdir(const char *pathname, mode_t mode);

目录的权限位,至少要设置一个执行许可位。即 rwx 中的那个 x 位。如果不设置,就没办法使用 cd 命令进入目录,也无法读取目录下的文件内容。所以,这个位是必须的。
目录的写权限位。如果未设置写权限位(w 位),将导致无法向目录文件中写入目录项(dir_entry 结构)。比如不能使用 touch 命令创建文件。
目录的读权限位。如果未设置读权限位,将导致无法读取目录文件中的目录项。比如你不能使用 ls 命令查看目录项的具体内容。
此函数不能创建多级目录

rmdir:

int rmdir(const char *pathname);

此函数只能删除空目录

opendir

DIR *opendir(const char *name);

打开目录,返回目录文件。

readir

struct dirent *readdir(DIR *dirp);

readdir 函数将从当前的目录项偏移的位置开始,读取一条目录项,同时将偏移量增加到下一目录项的位置。
dirent类似与dir_entry数据结构

struct dirent {
    ino_t          d_ino;       // inode number
    off_t          d_off;       // not an offset; see NOTES
    unsigned short d_reclen;    // length of this record
    unsigned char  d_type;      // type of file; not supported by all filesystem types */
    char           d_name[256]; // filename
};

你可能感兴趣的:(学习笔记)