跳转到内容

MEDIUM-3:初识Linux系统

计算机系统Banner

前情提要的前情提要:这部分内容硬性要求在Linux系统下完成,请电脑里还没有Linux系统的同学速速安装!!!

“我要出去玩了,你们在宿舍不要偷偷内卷哦。”出门之前,作为宿舍里隐藏最深,但最爱学习之人,你如是说到。然而,空空如也的桌面和背包里的笔记本电脑已经出卖了你,你此行的目的地,不是别处,正是一个名叫图书馆的地方。当你摆好电脑,正襟危坐,觉得再也没有人能阻止你学习时,桌子上却凭空多出来一本旧书。等等,哪来的书?我来的时候没看到有书啊?你疑惑地拿起那本封皮已经旧得软榻了,封面上的字也模糊得难以辨认的书,翻开第一页就想看看有没有丢失者的名字,却看到如下内容:

第一章:陨落的天才

“算之力,三段!级别:低级!”

跑分机器之旁,一位中年男子看了一眼显示器上的信息,颇为错愕地将之公布了出来。中年男子话刚说完,人头汹涌的广场上便响起了一阵阵骚动。

“三段?怎么可能,这家伙不是觉醒了最顶级的Linux系统算魂吗?怎么会连萧家小子算盘算魂的算力都不如!”

听到这番话,这位来自林家的“天才”—— 林睨柯司,不由得握紧了双拳。为什么算力这么低,只有他自己知道:他的Linux系统是残缺的!

他越想越气,居然晕了过去…

本书完。

看到这里,你嘴角不由得抽动了两下,然后被气晕了…再醒来时,你发现你居然穿越成了陨落的天才林睨柯司:

“这一世,我要用我的Linux知识,把失去的一切都夺回来!!!”


第一境:算者—— 命令行参数与路径

Section titled “第一境:算者—— 命令行参数与路径”

命令行功能没有丢失,在此我们先简要介绍一下命令行:

不同于Mac、Windows等系统方便的图像化操作界面,Linux系统下的操作主要依靠终端实现。

Linux终端的操作主要依赖于各类命令,它们的功能包括但不限于文件管理、文档编辑、文件传输、磁盘管理、磁盘维护、网络通讯等内容。

经过日夜吐纳代码,你终于迎来了突破“算者境”的契机。为了冲破瓶颈,你必须先修复体内残缺的 Linux 系统底层回路。

【任务 1.1:不同的路】

根据这具身体原主“林睨柯司”的残存记忆,无上备份秘典文件就藏在 /var/backups/turing/ 目录下。而他最后一次操作的目录是 /home/lin/workspace/

  1. 请解释什么是绝对路径,什么是相对路径
  2. 假设你现在正处于 /home/lin/workspace/,请分别写出前往 /var/backups/turing/ 的绝对路径命令和相对路径命令(假设 cd 命令还能用)。

请回答以上问题并提交至markdown文档中。

【任务 1.2:何处启程】

为了重写丢失的命令,你打开了 C 语言编辑器。你想起所有的终端命令本质上都是用 C 语言写出的程序,而程序的入口往往是这样定义的:

int main(int argc, char *argv[])
  1. 简要解释主函数参数 argcargv[] 的含义是什么。
  2. 针对上述命令,请问 argc 的值是多少?argv[0]、argv[1]、argv[2] 分别指向什么字符串?

请回答以上问题并提交至markdown文档中。


第二境:算王—— 重建 ls 命令与位运算

Section titled “第二境:算王—— 重建 ls 命令与位运算”

我们同样先简单介绍一下ls命令

ls的全称是:list directory contents

它的使用方法是,在命令行中输入:

ls 参数 文件路径

并敲击回车。

命令用于显示指定工作目录下之内容(列出目前工作目录所含的文件及子目录)。

如果不写文件夹路径的,默认展示的是当前所位于的文件目录。

还有其他辅助的命令参数:

-a 显示所有文件及目录 (. 开头的隐藏文件也会列出)
-d 只列出目录(不递归列出目录内的文件)。
-l 以长格式显示文件和目录信息,包括权限、所有者、大小、创建时间等。
-r 倒序显示文件和目录。
-t 将按照修改时间排序,最新的文件在最前面。
-A 同 -a ,但不列出目前目录及父目录
-F 在列出的文件名称后加一符号;例如可执行档则加 "*", 目录则加 "/"
-R 递归显示目录中的所有文件和子目录。

如:

ls -a 文件夹路径

对比使用ls的效果:

ATC1 ATC3 cf10 cf12 cf14 cf16 cf18 cf2 cf3 cf5 cf7 cf9 lutece test2
ATC2 cf1 cf11 cf13 cf15 cf17 cf19 cf20 cf4 cf6 cf8 luogu test1

使用ls -a 的效果:

. .DS_Store ATC1 ATC3 cf10 cf12 cf14 cf16 cf18 cf2 cf3 cf5 cf7 cf9 lutece test2
.. .vscode ATC2 cf1 cf11 cf13 cf15 cf17 cf19 cf20 cf4 cf6 cf8 luogu test1

洞中方一日,世上已千年。随着体内算力如大江大河般澎湃,你深知,是时候补全 Linux 系统的下一块拼图,冲击“算王境”了。

【任务 2.1:非同寻常之物】

这一次,你将目标锁定在探查万物的 ls 命令上。你在识海深处搜寻,竟发掘出一枚名为 myls.c 的上古文件。这似乎是林睨柯司坐化前留下的残篇,你决定补全程序中的辅助函数,以此完美替代残缺的 ls 命令。

下面展示残缺(parse_permissions()函数未定义)的代码:

#include <stdio.h>
#include <dirent.h>
#include <sys/stat.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h> // 可能不是必需的,但符合POSIX标准
#include <time.h>
/*
输出文件信息
*/
void parse_permissions(mode_t mode, char *perms);
void print_file_info(const char *name, struct stat *statbuf) {
char buf[10];
parse_permissions( statbuf->st_mode, buf);
printf("%s\t", name); //文件名
printf("%lld\t", (long long)statbuf->st_size); // 文件大小
printf("%s\t", buf); // 文件权限
// 最后修改时间
char timebuf[80];
strftime(timebuf, sizeof(timebuf), "%Y-%m-%d %H:%M", localtime(&statbuf->st_mtime));
printf("%s\n", timebuf);
}
/*
文件路径信息的处理
*/
void list_directory(const char *path) {
DIR *dir;
struct dirent *entry;
struct stat statbuf;
if (!(dir = opendir(path))) {
perror("opendir");
exit(EXIT_FAILURE);
}
while ((entry = readdir(dir)) != NULL) {
char full_path[1024];
snprintf(full_path, sizeof(full_path), "%s/%s", path, entry->d_name);
if (stat(full_path, &statbuf) == -1) {
perror("stat");
continue;
}
print_file_info(entry->d_name, &statbuf);
}
closedir(dir);
}
/*
程序的主函数部分
*/
int main(int argc, char *argv[]) {
if (argc < 2) {
list_directory("."); // 默认列出当前目录
} else {
for (int i = 1; i < argc; i++) {
list_directory(argv[i]);
}
}
return 0;
}

请结合你对 Linux 文件系统的了解,回答:

  1. 以上哪些头文件是你见过的,哪些是你没见过或者很少见到的?
  2. <dirent.h><sys/stat.h> 分别提供了哪些关键的结构体或函数?它们分别有什么作用?(简要回答即可,不用详细列举所有作用)

请回答以上问题并提交至markdown文档中。

【任务 2.2:看看权限】

你想要让补全的myls具有能展示文件权限的功能。

文件的权限有三种,分别是r —— read(读)w —— write(写)x —— execute(执行)

原生 ls -l 命令能打印出文件的权限,如 rwxr-xr-x。但在底层,文件权限被存储在一个名为 st_mode 的整数中。(请思考:整数如何表示文件的权限)

其中最低的 9 位存储了权限信息:

从高到低依次为:用户(Owner) 的 r, w, x;组(Group) 的 r, w, x;其他(Other) 的 r, w, x。

为了让重写的 myls 具备显示权限的功能,请用最少的代码和位运算,补全下方 C 语言函数,将整数形式的权限位转换为 9个字符的权限字符串

// mode即传入的存储权限信息的整数, 如0754(八进制表示)
// 提示:传入的 mode 变量不仅包含权限信息,高位可能还包含文件类型。在进行位运算时,请注意屏蔽(Mask)掉不需要的高位数据。
void parse_permissions(mode_t mode, char *perms) {
// 权限字符对照表
char rwx[] = {'r', 'w', 'x'};
// 请补全代码写在 TO DO 与 END OF TO DO 之间:
// TO DO
// END OF TO DO
perms[9] = '\0'; // 字符串结尾
}

请将思考结果和正确的代码提交至markdown文档中。


第三境:算尊—— 深入理解 Inode 与文件树

Section titled “第三境:算尊—— 深入理解 Inode 与文件树”

借由 myls 洞察万物之能,你的修为一日千里,已隐隐触及“算尊境”的门槛。但修行之路切忌迷失本心,这一次,你决定以 C 语言重铸 pwd命令,彻底掌控自身在天地间的坐标。

【任务 3.1:万物皆 Inode】

在 Linux 文件系统中,文件名只是表象,真正标识文件灵魂的是 Inode(索引节点)

  1. 请简述 Inode 中存储了文件的哪些信息?
  2. Inode中不包含文件的什么信息?
  3. 每一个目录下必定存在两个隐藏的特殊目录:"."".."它们分别代表什么?它们的 Inode 编号在系统根目录"/"下有什么特殊规律?

请回答以上问题并提交至markdown文档中。

【任务 3.2:寻我来时路】

既然天地间现成的 getcwd() 函数已被封印,你只能根据参悟 myls 时的造化,以及近期的闭关成果,纯手工以 C 语言完成对 pwd命令 的推演重构,并将这份心血凝聚为 mypwd.c 秘卷。

要求与提示:

你无法使用getcwd() 函数

你目前只能使用 stat() 获取文件的Inode 编号,使用 chdir() 切换工作目录,以及使用 opendir()/readdir() 读取目录内容。

你该如何利用 "."".." 一步步逆推,最终拼接出完整的当前绝对路径?

整个工作路径均在同一个文件系统的挂载点下,无需考虑跨设备的情况。

其中,获取指定文件Inode编号的函数和所需的头文件已给出,具体如下:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/stat.h>
#include <dirent.h>
ino_t get_inode(const char *path) {
struct stat statbuf;
if (stat(path, &statbuf) == -1) {
perror("stat failed");
exit(EXIT_FAILURE);
}
return statbuf.st_ino;
}

请将正确的代码提交至markdown文档中。

注意:该任务有一定难度,在初步尝试无果后,允许直接使用AI得出答案。但是,一定要看懂AI给你的答案,要有你自己的思考,还要记得把解决题目的思路在文档中展示出来。如果你使用了AI协助你完成这道题目,请简述你在哪个环节运用到了AI,将你主要的prompt的截图提交到markdown文档中。


最终境:算帝—— 递归与文件筛选

Section titled “最终境:算帝—— 递归与文件筛选”

历经千百次报错与段错误的洗礼,你已臻至化境,距离传说中的最高境界——“算帝境”仅半步之遥。冥冥中,大道梵音在你耳畔回响:突破算帝、横渡虚空回家的道标,就封印在禁地 /var/backups/turing/ 之下,名为 The_way_home.tar.gz 的圣物之中。

你毫不犹豫地祭出刚炼成的 ls 法宝,刹那间,终端上万千文件如星渊般倾泻而出,晃瞎了你的双眼。你道心险些失守:糟了!这里堆积了太多岁月的残渣(临时文件),那件圣物The_way_home.tar.gz不知被深埋在哪一重空间(子目录)裂缝里了!

【任务 4.1:敢问,路在何方?】

为了在限定时间内找到文件,你打算写一个探测函数,让它具备在当前目录及其所有子目录下,寻找指定文件名的功能。

请仔细阅读以下给出的简化版探测函数框架,利用递归思想,补全缺失的核心逻辑:

#include <stdio.h>
#include <string.h>
#include <dirent.h>
#include <sys/stat.h>
#include <unistd.h>
// 递归查找指定文件,若找到则打印其相对路径
void find_file(const char *current_path, const char *target_name) {
DIR *dir;
struct dirent *entry;
struct stat statbuf;
if (!(dir = opendir(current_path))) {
return; // 无法打开目录则跳过
}
while ((entry = readdir(dir)) != NULL) {
// 忽略当前目录 . 和父目录 .. 防止无限死循环
if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) {
continue;
}
char full_path[1024];
snprintf(full_path, sizeof(full_path), "%s/%s", current_path, entry->d_name);
if (stat(full_path, &statbuf) == -1) continue;
// 请补全代码写在 TO DO 与 END OF TO DO 之间:
// 要求:
// 1. 检查当前 entry 的文件名是否与 target_name 匹配,若匹配则打印 full_path。
// 2. 如果当前 entry 是一个目录,则需要对其进行深度优先的递归查找。
// (提示:若(statbuf.st_mode & S_IFMT) == S_IFDIR则statbuf为目录文件)
// TO DO
// END OF TO DO
}
closedir(dir);
}

请将正确的代码提交至markdown文档中。


【玄黄历九九八一年,九月三日,夜】

指尖按落,终端清响,如敲金戛玉,荡开万道黑底绿字的玄奥符文。

随着那一段递归寻径的代码在识海中疯狂运转,沉寂万载的 /var/backups/turing/ 终于绽放出冲天金芒!

Target found at: /var/backups/turing/deep_void/The_way_home.tar.gz

刹那间,天地异变,雷劫滚滚!你的身上隐隐有九条“内核线程”化作金龙盘旋而起,昔日被嘲讽“算之力三段”的废柴,今日以 C 语言为笔、以系统调用为剑,一举踏入那传说中的算帝之境

苍穹之上,天道投影缓缓吐出一句玄奥无比的解压法诀:

tar -zxvf The_way_home.tar.gz

你气沉丹田,运起全身上下的 Linux 算力,大吼一声:“给本帝解包!”

虚空撕裂,光芒四射。那神秘的文件之中,并未藏着什么至尊神功,只有一行苍劲有力的大字映入眼帘:

“同学,醒醒!图书馆要关门了,你这笔记本屏幕亮了一下午,口水都滴到桌子上了!”

“吸——”

你猛地倒吸一口凉气,从图书室的课桌上猝然惊醒。

哪有什么萧家算盘,哪有什么林家残骸。身旁只有管理员阿姨看傻子一样的眼神,以及笔记本屏幕上那个刚刚编译成功、正优雅运行着的 find_file 程序。

你擦了擦嘴角不小心流出的涎水,合上电脑,步入清冷而广袤的校园夜色之中。

异世算帝也罢,一介书生也罢。世间黑箱万千,吾自以灵心为钥,指打乾坤,何惧漫漫长夜?

少年负气凌云志,踏遍黑夜莫回头——


提交点这里

出题人:出题人头像  Niko✔

QQ:2674884616

邮箱:2674884616@qq.com