pwd命令的作用

Linux的文件系统比较庞大,所以笔者从pwd这一命令入手,在实现的过程中加深对文件系统的了解。

输入:man pwd

从指导文档中可以看到,pwd命令的作用是显示出当前所处位置,以路径的形式打印出来。

举例如下:

笔者首先输入pwd命令,显示出/home/lularible,说明我当前就处在该位置。

当笔者进入其中的一个子目录"bin"时,再次输入pwd命令,显示出/home/lularible/bin,这是显而易见的。

Linux文件系统内部结构

为了能够实现pwd命令,就需要先了解Linux文件系统的内部结构

文件系统的多层抽象

一般而言,文件是存储在硬盘上的,那么将磁盘这一物理实体,进行逻辑划分和组织,就是进行抽象的过程。目的就是为了便于管理。

最朴素的管理手段就是,给硬盘的区域编号,按照编号从低到高给文件分配存储空间。当然,访问效率就可想而知了(当文件数量一多,访问效率将会及其低下)。所以那些系统开发人员就发挥出自己的聪明才智,对文件系统进行了巧妙的设计。

第一层抽象:从磁盘到分区

一整块磁盘,能存储大量的数据,统一管理起来不太方便。若能将它分而治之,则更加灵活。比如说window系统里面的磁盘分区:C盘,D盘,E盘等等。每个分区都可以看作是一个独立的磁盘。

第二层抽象:从磁盘到块序列

一个磁盘由一些磁性盘片组成。每个盘片的表面都被划分为很多同心圆,这些同心圆称作磁道,每一个磁道又进一步被划分为扇区,每个扇区可以存储一定字节的数据(如512字节)。扇区是磁盘上的基本存储单元,每次空间分配或者访问都是以一个扇区为最小单位。

第三层抽象:从块序列到三个区域的划分

为了存储不同类型的数据(文件内容、文件属性、目录),将磁盘块分为3个部分:

  • 1.超级块:文件系统中的第一个块被称为超级块,用来存放文件系统本身的结构信息。
  • 2.i-节点表:文件系统的下一个部分被称为i-节点表。每个文件都有一些属性,如大小、文件所有者和最近修改时间等。这些性质被记录在一个称为i-节点的结构中。所有的i-节点都有相同的大小,并且i-节点表是这些结构的一个列表。
  • 3.数据区:文件系统的第三部分是数据区。文件的内容保存在这个区域。

创建一个文件的过程

文件有内容和属性,内核将文件内容存放在数据区,文件属性存放在i-节点,文件名存放在目录。下图所示为创建一个文件的例子:

其中的几个主要操作为:

  • 1.存储属性:内核找到一个空的i-节点(图中为47),把文件信息记录到该i-节点中。
  • 2.存储数据:内核从空闲磁盘块表中获得627、200、992这三个磁盘块,将内核缓冲区中的数据复制到这三个磁盘块中。
  • 3.记录分配情况:文件内容按序存放在磁盘块627/200/992中。内核在i-节点的磁盘分布区记录了上述序列块。磁盘分布区是一个磁盘块序号的列表,这三个编号放在最开始的三个位置。
  • 4.添加文件名到目录:新文件的名字是userlist。内核将入口(47,userlist)添加到目录文件。文件名和i-节点号之间的对应关系将文件名和文件的内容及属性连接了起来。

目录的结构

目录被抽象为一个包含i-节点号和文件名的表。

输入ls -ia可以查看当前目录包含的文件以及对应的i-节点号:

如代表当前目录的"."的i-节点号为1048600,代表上一级目录的".."的i-节点号为936372。再看一下根目录的组成:

其中"."和".."的i-节点号都是2,说明根目录的上级目录就是自己。另外,还可以看到存在i-节点号一样的文件(目录),这就是硬链接,将在后续的软、硬链接区别一节中叙述。

文件读取的工作原理

既然知道了目录的结构,那么就可以理一遍文件读取的过程了。举例来说:

输入cat userlist,表示读取userlist这个文件并显示。

step1.在目录中寻找文件名

文件名存储在目录文件中,内核在目录文件中寻找包含字符串"userlist"的记录。userlist所在的记录包含编号为47的i-节点。

step2.定位i-节点并读取内容

内核在文件系统中的i-节点区域找到i-节点47。i-节点包含数据块编号的列表。

step3.访问存储文件内容的数据块

在step2之后,即知道了数据块的位置和在磁盘中的顺序,就可以通过不停的调用read函数,使内核不断将字节从磁盘复制到内核缓冲区。

ps:读取权限的控制:内核根据文件名找到i-节点号,再根据i-节点号找到i-节点。在i-节点中,找到文件的权限位和用户ID,从而判断该用户是否具有读取权限。

大文件的i-节点

每个文件对应一个i-节点,而每个i-节点大小是固定的,所以当文件内容需要占用很多磁盘块时,i-节点中的磁盘分布区可能不够用。这个时候,就需要采用多级分配的方式。一图说明问题:

其中,级数越多,访问磁盘时就越慢。因为需要按照每一级的指引,多次读取磁盘。

实现pwd

思路

  • 1.获得当前目录的i-节点号,记为stat
  • 2.改变目录至上级目录
  • 3.找到i-节点号stat的名字

重复上述步骤,直到树顶。到树顶的判断依据:一个目录的"."和".."的i-节点号相同。

源代码

#include<stdio.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<dirent.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h> ino_t get_inode(char*);
void printpathto(ino_t);
void inum_to_name(ino_t,char*,int); int main()
{
printpathto(get_inode(".")); //打印当前位置(文件路径)
putchar('\n');
return 0;
} void printpathto(ino_t this_inode)
{
ino_t my_inode;
char its_name[BUFSIZ];
if(get_inode("..") != this_inode)
{
chdir(".."); //往上一级
inum_to_name(this_inode,its_name,BUFSIZ); //获得i-节点号对应的文件名
my_inode = get_inode("."); //获得当前目录节点号
printpathto(my_inode); //递归打印
printf("/%s",its_name); //打印文件名
}
} //获得i-节点号对应的文件名
void inum_to_name(ino_t inode_to_find,char* namebuf,int buflen)
{
DIR* dir_ptr;
struct dirent* direntp;
dir_ptr = opendir(".");
if(dir_ptr == NULL){
perror(".");
exit(1);
}
while((direntp = readdir(dir_ptr)) != NULL){
if(direntp->d_ino == inode_to_find){
strncpy(namebuf,direntp->d_name,buflen);
namebuf[buflen-1] = '\0';
closedir(dir_ptr);
return;
}
}
fprintf(stderr,"error looking for inum %ld\n",inode_to_find);
exit(1);
} //获得文件名对应的i-节点号
ino_t get_inode(char* fname)
{
struct stat info;
if(stat(fname,&info) == -1){
fprintf(stderr,"Cannot stat");
perror(fname);
exit(1);
}
return info.st_ino;
}

当pwd01命令运行在装载文件系统中时,可能会出现与原版pwd不同的结果。(装载文件系统是指将它嵌入到已有的系统以获得某些支持,子树的根目录被嵌入到根文件系统的一个目录中,子树所在的目录被称为第二个系统的装载点。)依据pwd01命令的实现逻辑,当遇到当前目录和上级目录i-节点号一样时,就停止上溯,实际上可能只是到达了一个被装载的文件系统的顶端,而非整个文件系统的顶端。

硬链接和软链接(符号链接)

假设多个文件名通过硬链接的方式指向同一个文件,那么它们拥有同一个i-节点,系统会记录链接数。如果删除其中的一个文件,则会将链接数减一,直到链接数为0时,才会真正删除文件。

而建立原文件的软链接,则其i-节点号与原文件的不同,并且链接数、修改时间和文件大小都不同于原始文件。若原始文件被删除或者位置变动或者被改名,那么软链接将指向空。这种思想有点类似于window中的快捷方式。

参考资料

《Understanding Unix/Linux Programming A Guide to Theory and Practice》

欢迎大家转载本人的博客(需注明出处),本人另外还有一个个人博客网站:lularible的个人博客,欢迎前去浏览。

Linux系统编程【4】——文件系统的更多相关文章

  1. Linux系统编程@进程通信(一)

    进程间通信概述 需要进程通信的原因: 数据传输 资源共享 通知事件 进程控制 Linux进程间通信(IPC)发展由来 Unix进程间通信 基于System V进程间通信(System V:UNIX系统 ...

  2. Linux 系统编程

    简介和主要概念 Linux 系统编程最突出的特点是要求系统程序员对它们工作的的系统的硬件和操作系统有深入和全面的了解,当然它们还有库和系统调用上的区别. 系统编程分为:驱动编程.用户空间编程和网络编程 ...

  3. 读书笔记之Linux系统编程与深入理解Linux内核

    前言 本人再看深入理解Linux内核的时候发现比较难懂,看了Linux系统编程一说后,觉得Linux系统编程还是简单易懂些,并且两本书都是讲Linux比较底层的东西,只不过侧重点不同,本文就以Linu ...

  4. Linux系统编程【转】

    转自:https://blog.csdn.net/majiakun1/article/details/8558308 一.Linux系统编程概论 1.1 系统编程基石 syscall: libc:标准 ...

  5. 《Linux系统编程(第2版)》

    <Linux系统编程(第2版)> 基本信息 作者: (美)Robert Love 译者: 祝洪凯 李妹芳 付途 出版社:人民邮电出版社 ISBN:9787115346353 上架时间:20 ...

  6. linux系统编程(一)概述

    glibc库封装了linux系统调用,并提供c语言接口 所以学习linux系统编程,主要参考glibc库系统调用相关api 一.进程控制: fork 创建一个新进程 clone 按指定条件创建子进程 ...

  7. Linux系统NFS网络文件系统

    Linux系统NFS网络文件系统 NFS(network file system)网络文件系统,就是通过网络让不同的主机系统之间可以共享文件或目录,此种方法NFS客户端使用挂载的方式让共享文件或目录到 ...

  8. Linux系统编程温故知新系列 --- 01

    1.大端法与小端法 大端法:按照从最高有效字节到最低有效字节的顺序存储,称为大端法 小端法:按照从最低有效字节到最高有效字节的顺序存储,称为小端法 网际协议使用大端字节序来传送TCP分节中的多字节整数 ...

  9. linux系统编程之错误处理

    在linux系统编程中,当系统调用出现错误时,有一个整型变量会被设置,这个整型变量就是errno,这个变量的定义在/usr/include/errno.h文件中 #ifndef _ERRNO_H /* ...

  10. LINUX系统编程 由REDIS的持久化机制联想到的子进程退出的相关问题

    19:22:01 2014-08-27 引言: 以前对wait waitpid 以及exit这几个函数只是大致上了解,但是看REDIS的AOF和RDB 2种持久化时 均要处理子进程运行完成退出和父进程 ...

随机推荐

  1. centos nc命令安装

    yum install nc.x86_64 nc命令的参数 参数 作用-i 设置数据报传送时间间隔-l 以服务器方式运行-k 重复接收并处理某个端口上的所有连接,必须与-l选项一起使用-n 使用ip地 ...

  2. easyui layout 收缩的bug

    easyui layout提供collapse方法折叠指定的 panel,'region' 参数可能的值是:'north'.'south'.'east'.'west',但是在 IE6的环境下,调用这个 ...

  3. lintcode:Pow(x, n)

    Pow(x, n) Implement pow(x, n). 解题 直接顺序求解,时间复杂度O(N) public class Solution { /** * @param x the base n ...

  4. poj 1004 Financial Management

    求平均数,记得之前在杭电oj上做过一个求平均数的题目,结果因为题目是英文的,我就懵逼了 #include <stdio.h> int main() { ; double num; int ...

  5. POJ 2001-Shortest Prefixes(Trie 入门)

    题意:给你一组串,求每个串在组中唯一标志的最短前缀 分析:保存树上经过各节点的单词个数,扫描每个串当经过节点单词个数是一(能唯一标志)结束 #include <map> #include ...

  6. 用document.getElementsByTagName()返回的真的是数组吗?

    document.getElementsByTagName()返回的真的是数组吗? 这是这几天开发中遇到的问题. 一个如下的HTML结构: <ul> <li> <li&g ...

  7. 【转】整理一下Android中的ListView

    原文网址:http://sunbofu.blog.51cto.com/6431507/1280441 Android中的listview目测是一个使用频率很高的组件,所以今天来总结一下listview ...

  8. Xcode 中添加 .pch文件

    1  新建工程 2  创建  .pch文件 3   在setting里面进行设置:

  9. Oracle\MS SQL Server Update多表关联更新

    原文:Oracle\MS SQL Server Update多表关联更新 一条Update更新语句是不能更新多张表的,除非使用触发器隐含更新.而表的更新操作中,在很多情况下需要在表达式中引用要更新的表 ...

  10. smarty模板调数据库并做添加删除修改和分页

    smarty模板只要就是实现分离效果所以每个功能都需要两个页面一个是HTML  和 PHP  两部分组成 使用smarty模板要在main文件夹下面创建login.php文本,要用smarty模板首先 ...