Linux系统编程学习笔记(3)-Linux文件系统与相关函数调用

Linux文件系统的内部结构

硬盘实际上是由一些磁性盘片组成的计算机系统的一个设备。文件系统是对该设备的一种多层次的抽象。
第一层抽象:一个磁盘能够储存大量的数据,一个磁盘可以被划分成分区,每个分区可以看作是一个独立的磁盘。
第二层抽象:一个硬盘有一些磁性盘片组成。每个盘片的表面都被划分为很多同心圆,这些同心圆称作磁道,每个磁道又被进一步划分为扇区,每个扇区可以储存一定字节数的数据。扇区是磁盘上的基本储存单元。一个磁盘扇区编号的系统使得我们可以把磁盘视为一系列块的组合。
第三层抽象:文件系统可以用来储存文件内容,文件属性和目录Unix把磁盘块分成了3个部分来储存上述信息:
这里写图片描述

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

文件有内容和属性,内核将文件内容存放在数据区, 文件属性存放在i-节点,文件名存放在目录。
这里写图片描述

可以根据上图来描绘根据文件名读取文件内容的过程。

  1. 系统根据目录名或文件名找到文件对应的i-节点的序号。
  2. 根据序号确定文件内容储存在磁盘的块序号。
  3. 根据块序号定位磁盘内容。

有两个比较特别的i-节点,分别是“.”和“..”,前者代表本目录的i-节点序号,后者代表父目录的i-节点。shell命令pwd就是通过遍历“.”与“..”的i-节点序号来得到文件目录的。另外,目录顶端的“.”与“..”的i-节点序号相同,实际应用中经常根据这一特性来判断是否目录遍历到了顶端。

与目录树相关的系统调用

mkdir

命令mkdir用来创建新的目录,它接受命令行上的一个或多个目录名,使用mkdir系统调用:

#include <sys/stat.h>
#include <sys/types.h>
/*****************************
param pathname: 新目录名
param mode:权限位的掩码
return 0 if successed else return -1 
*****************************/
int result = mkdir(char *pathname, mode_t mode)

rmdir

命令rmdir用来删除一个目录。它接受命令行上一个或者多个目录名,使用rmdir系统调用

#include <unistd.h>
/*****************************
param path: 目录名
return 0 if successed else return -1 
*****************************/
int result = rmdir(const char *path)

rm

命令rm用来从一个目录文件中删除一个记录,它接受命令行上一个或者多个文件名,使用unlink系统调用

#include <unistd.h>
/*****************************
param path: 需要删除的链接名
return 0 if successed else return -1 
*****************************/
int result = unlink(const char *path)

unlink函数不能被用来删除目录, 它会减少相应i-节点的连接数。

ln

命令ln用来创建一个文件的链接,使用系统调用link

#include <unistd.h>
/*****************************
param orig:原始链接的名字
parm new:新建链接的名字
return 0 if successed else return -1 
*****************************/
int result = link(const char *orig, const char *new)

link生成一个i-节点的链接。新链接包含原始链接的i-节点号并且具有特定的名字。注意:link不能用来生成目录的新链接

mv

命令mv用来改变文件和目录的名字或位置,mv命令使用系统调用rename:

#include <unistd.h>
/*****************************
param from: 原始链接的名字
param to:新建链接的名字
return 0 if successed else return -1 
*****************************/
int result = rename(const char *from, const char *to)

cd

cd用来改变进程的当前目录,cd会对进程产生影响,但是不会影响目录。cd命令使用系统调用chdir:

#include <unistd.h>
/*****************************
param path: 要到达的目录
return 0 if successed else return -1 
*****************************/
int result = chdir(const char *path)

编写pwd命令

#include <iostream>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <dirent.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
using namespace std;

ino_t get_inode(char*);
void printpathto(ino_t);
void inum_to_name(ino_t, char*, int);

int main(int argc, char *argv[])
{
  printpathto(get_inode("."));
  putchar('\n');
  return 0;
}

void printpathto(ino_t this_inode){
  ino_t my_inode;
  char its_name[100];
  if(get_inode("..") != this_inode){            //根据i-节点号获取父目录
      chdir("..");                              //进入父目录
      inum_to_name(this_inode, its_name,100);   //在父目录中搜寻子目录名
      my_inode = get_inode(".");
      printpathto(my_inode);                    //递归调用直到到达目录顶层
      printf("/%s", its_name);
    }
}

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 %d\n", inode_to_find);
  exit(1);
}

ino_t get_inode(char *fname){     //获取i-节点序号
  struct stat info;
  if(stat(fname,&info) == -1){
      fprintf(stderr, "Cannot stat");
      perror(fname);
      exit(1);
    }
  return info.st_ino;
}

猜你喜欢

转载自blog.csdn.net/qq_34561506/article/details/78250014