理解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文件。