Linux文件

理解Linux文件

简介

        Linux系统有一个重要的而且非常著名的概念:一切皆文件
        在Linux中,一切都是文件,不论是CPU,或是硬盘抑或是显卡,在Liunx中都是文件。想要学好Linux,对文件的理解必不可少,在这里就谈谈Linux文件。

概述

        在Linux中,文件系统是一个树形结构,利用tree这个软件,我们可以很方便的查看这一结构:
安装tree

  • Ubuntu/Debian:
    sudo apt-get install tree
  • RetHat/CentOS:
    sudo dnf install tree

使用命令查看树形结构:
在这里插入图片描述
/bin 目录是包含一些二进制文件的目录,即可以运行的一些应用程序。 你会在这个目录中找到 ls等 程序,以及用于新建和删除文件和目录、移动它们基本工具。还有其它一些程序,等等。

/boot 目录包含启动系统所需的文件。千万千万别乱动这个文件夹

/dev 目录包含设备文件。 其中许多是在启动时或甚至在运行时生成的。显卡和硬盘,甚至是你插入的U盘都放在这里。

/etc 的目录名称会让人变得非常的困惑。/etc 来自于 Unix 系统,它的字面意思是 “etcetera”(诸如此类) ,因为它是系统文件管理员不确定在哪里放置的文件的垃圾场。
而对于现在的Linux,/etc“要配置的所有内容(Everything To Configure)”应该更为恰当,因为它包含大部分的系统配置文件。

/home 是你可以找到用户个人目录的地方。

/lib 是库文件所在的地方。gcc,java甚至python,它们都被放在这里。
其实,在文件系统周围散布着更多的 lib 目录,但是这个直接挂载在/ 的**/lib**目录是特殊的,除此之外,它包含了所有重要的内核模块。 内核模块是使你的显卡、声卡、WiFi、打印机等工作的驱动程序。

时间关系,这里就不再赘述其他文件夹的作用了,感兴趣的请查阅资料。这里给出在wiki上的一张图片概括:
在这里插入图片描述

Linux文件结构

一个标准的Linux文件由一个目录项,一个inode和数据区域构成。
Inode包含文件的属性(如读写属性、owner等,以及指向数据块的指针),数据区域块则是文件内容。
当查看某个文件时,会先从inode table中查出文件属性及数据存放点,再从数据块中读取数据。
如图:
在这里插入图片描述
其中,目录项的结构是文件名+Inode编号
目录项和Inode统称为元数据

利用stat函数,我们可以很方便的查看文件的元数据信息,下面是CSAPP Chapter10 p632页给出的stat数据结构:
在这里插入图片描述
下面给出一个示例代码,它将从命令行获取一个参数,识别它是文件夹还是文件,如果是文件,它是否可读(来自书上P633面)

/* demo.c */
#include <stdio.h>
#include <sys/stat.h>

int main(int argc, char **argv) {
    struct stat s;
    char *      type, *readok;
    stat(argv[ 1 ], &s);
    if (S_ISREG(s.st_mode)) {
        type = "regular";
    } else if (S_ISDIR(s.st_mode)) {
        type = "directory";
    } else {
        type = "other";
    }
    if ((s.st_mode & S_IRUSR)) {
        readok = "yes";
    } else {
        readok = "no";
    }
    printf("type = %s, read = %s\n", type, readok);
    return 0;
}

测试:
在这里插入图片描述
在这里插入图片描述

实战

基于上面和书上的内容,使用C/C++语言写出一个能够查看一个文件夹下所有文件的名称(包括子文件夹中的文件)

思考

在学习Java的时候,写过类似的程序,使用深度优先搜索广度优先搜索遍历即可,程序如下:

import java.io.File;
import java.io.IOException;
import java.util.Arrays;
import java.util.LinkedList;

public class ShowFiles {
    public static void main(String[] args) {
    	try {
        	File f = new File(args[0]);
        } catch (IOException e) {
        	e.printStackTrace();
        }
        showDir_BFS(f);
        // showDir_DFS(f);
    }

    public static void showDir_DFS(File dir) {
        File[] files = dir.listFiles();
        for (int i = 0; i < files.length; ++i) {
            if (files[i].isDirectory()) {
                showDir_DFS(files[i]);
            }
            else
                System.out.println(files[i]);
        }
    }

    public static void showDir_BFS(File dir) {
        LinkedList<File> queue = new LinkedList<>();
        // 初始化队列,先把根目录全部进入队列
        queue.addAll(Arrays.asList(dir.listFiles()));
        // 当队列不空,就继续BFS
        while (!queue.isEmpty()) {
            // 拿出一个元素
            File temp = queue.removeFirst();
            // 如果是目录,则把它的子文件(夹)全部加入队列中
            if (temp.isDirectory())
                queue.addAll(Arrays.asList(temp.listFiles()));
            // 否则就输出
            else
                System.out.println(temp);
        }
    }
 }

参考这个代码的思想,可以写出其C语言版本

解决

C语言代码如下:

/* fileTest.c */
#include <dirent.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
void readFile(char *basePath) {
    DIR *          dir;
    struct dirent *ptr;
    char           base[ 1000 ];

    if ((dir = opendir(basePath)) == NULL) {
        perror("Open dir error!");
        exit(1);
    }

    while ((ptr = readdir(dir)) != NULL) {
        /* current dir or parrent dir */
        if (strcmp(ptr->d_name, ".") == 0 || strcmp(ptr->d_name, "..") == 0) {
            continue;
        }
        /* file */
        else if (ptr->d_type == 8) {
            printf("d_name:%s/%s\n", basePath, ptr->d_name);
        }
        /* link file */
        else if (ptr->d_type == 10) {
            printf("d_name:%s/%s\n", basePath, ptr->d_name);
        }
        /* directory */
        else if (ptr->d_type == 4) {
            memset(base, '\0', sizeof(base));
            strcpy(base, basePath);
            strcat(base, "/");
            strcat(base, ptr->d_name);
            readFile(base);
        }
    }
    closedir(dir);
}

int main(void) {
    DIR *dir;
    char pathStr[ 1000 ];

    /* get the current absoulte path */
    memset(pathStr, '\0', sizeof(pathStr));
    getcwd(pathStr, 999);
    printf("the current dir is : %s\n", pathStr);

    /* get the file list */
    memset(pathStr, '\0', sizeof(pathStr));
    /* the test is my test dir, you can change that */
    strcpy(pathStr, "./test");
    readFileList(pathStr);
    return 0;
}

其中,需要了解dirent的数据结构:

 struct dirent
 {    
      long d_ino;       /* inode number 索引节点号 在<sys/types.h>文件该类型的描述*/
      off_t d_off;      /* offset to this dirent 在目录文件中的偏移 */
      unsigned short d_reclen;   /* length of this d_name 文件名长 */
      unsigned char d_type;      /* the type of d_name 文件类型 */
      char d_name [NAME_MAX+1];  /* file name (null-terminated) 文件名,最长256字符 */
 }

这段dirent的数据结构的代码来自:

https://blog.csdn.net/wangqingchuan92/article/details/80109793

同时,需要知道getcwd的作用:
在这里插入图片描述
对于dirent,重点在于d_type,我们可以直接查看它的源码:

#ifdef __USE_BSD
/* File types for `d_type'.  */
enum
  {
    DT_UNKNOWN = 0,
# define DT_UNKNOWN     DT_UNKNOWN
    DT_FIFO = 1,
# define DT_FIFO        DT_FIFO
    DT_CHR = 2,
# define DT_CHR         DT_CHR
    DT_DIR = 4,
# define DT_DIR         DT_DIR
    DT_BLK = 6,
# define DT_BLK         DT_BLK
    DT_REG = 8,
# define DT_REG         DT_REG
    DT_LNK = 10,
# define DT_LNK         DT_LNK
    DT_SOCK = 12,
# define DT_SOCK        DT_SOCK
    DT_WHT = 14
# define DT_WHT         DT_WHT
  };

这样就一目了然了

下面是测试情况:
在这里插入图片描述

总结

Linux文件系统是一个庞大的系统,其学习成本并不低,在这里展露的仅仅也是冰山一角。
由于时间的关系,这里就不再深入谈Linux文件了,今后有时间会补完这篇。同时,希望在今后的学习中更加深入的学习Linux文件。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值