教材第一章读书笔记
by 20201321 周慧琳
学习目标
- 第一章:引言
第一章的引言部分包涵Unix的历史、Linux的开发和各种版本(没有提及centOS和openeuler,我们可以自己补充学习),还列出了适用Linux的各种虚拟机。
我选用的是VMware因为我觉得它比vitrualbox功能更清晰好用。本章还介绍了文件系统组织、文件系统,和一些常用的命令。我认为这些都需要自己通过实践去掌握。 - 第二章:编程背景
第二章是关于系统编程需要的背景信息,介绍了vim、gedit、emacs文本编辑器。我认为有必要掌握一种,尤其是vim。
另外需要了解程序开发的步骤,包括GCC、静态动态链接、二进制可执行文件的格式和内容、程序执行和终止。
这章还需要我们认识许多基础知识,包括:函数调用时堆栈的使用,C语言常见错误(很多和指针误用有关),各类数据结构(链表、二叉树等)、还有GNU make工具和Makefile的编写方法。
此章有一个实践任务,即设计一个文件系统树,支持mkdir、rmdir、creat、rm、cd、pwd、ls操作。
知识图谱的归纳
对于第二章繁琐的知识点,利用xmind工具制作了一个知识图谱来强化知识理解
学习收获和心得
开始学这本书我感受最大的是这本书给了我们一个完整的学习架构和学习建议,但是要真正掌握其中的一个知识点还是需要实践。
无论是GNU make工具以及对应的makefile编写、vim编译器的实际使用等等,这些只看书是非常抽象的。有些时候,博客或者视频确实会比书更通俗易懂些。尽管我必须承认这本书提供的知识的严谨性是博客和视频无法比拟的。
以下是我重点学习的内容:
-
Linux文件系统
-
vim使用
【最强Vim新手指南,手把手教你打造只属于自己的代码编辑器!-哔哩哔哩】 https://b23.tv/L73Zptt
这本书对于vim的介绍比较少,主要是关于emac的,所以我在网上找到了这个视频。
他首先介绍了vim的定位、设计理念,然后介绍了他自己的vim快捷键配置,现在很多ide都有vim模式插件,
在简洁的vim模式下编辑速度大幅提升,vim在现在更多地已经不作为一种具体工具,而是一种习惯模式,实现不用鼠标只用键盘编辑代码。
-
make工具与Makefile的编写
参考https://blog.csdn.net/weixin_38391755/article/details/80380786进行学习
Windows的IDE都为我们做了这一块的工作,所以很多时候我们都没有重视这块知识。但在Unix下的软件编译,你就不能不自己写makefile了。之前我在ide里面也会发现Makefile文件,但是我并不知道这是干什么的。
我看了这本书里面的描述,又去查找了一下相关的博客,现在make和makefile让我想到了我在“计算机组成原理”里老师让我们用Verilog语言编写一个cpu中的寄存器文件,这个项目包括8位寄存器文件、译码器文件和选择器文件,这三个文件需要按一定的顺序编译和传递参数。我们需要写一个编译文件实例化、指明编译顺序和联系各个文件的参数。
而Makefile里面的依赖关系,编译规则,跟这些是底层逻辑差不多的。makefile关系到了整个工程的编译规则。一个工程中的源文件不计数,其按类型、功能、模块分别放在若干个目录中,makefile定义了一系列的规则来指定,哪些文件需要先编译,哪些文件需要后编译,哪些文件需要重新编译,甚至于进行更复杂的功能操作,因为makefile就像一个Shell脚本一样,其中也可以执行操作系统的命令。makefile带来的好处就是——“自动化编译”,一旦写好,只需要一个make命令,整个工程完全自动编译,极大的提高了软件开发的效率。make是一个命令工具,是一个解释makefile中指令的命令工具,一般来说,大多数的IDE都有这个命令,比如:Delphi的make,Visual C++的nmake,Linux下GNU的make。可见,makefile都成为了一种在工程方面的编译方法。
CC = gcc
CFLAGS = -O -Wall -m64 -std=gnu89
LIBS = -lm
all: main_max main_min #必须写成all这样的形式,否则只会生成前一个可执行文件main_max
main_max: main_max.c bar.o foo.o
$(CC) main_max.c bar.o foo.o -o main_max
main_min: main_min.c bar.o foo.o
$(CC) main_min.c bar.o foo.o -o main_min
foo.o: foo.c
$(CC) -c foo.c
bar.o: bar.c
$(CC) -c bar.c
.PHONY: clean
clean:
rm *.o main_max main_min
- 数据结构与Linux文件树模拟
大二上数据结构课我们已经学过链表、队列、二叉树这些数据结构,它们的描述和定义以及常见的删除节点、遍历方法我们都编写过C语言代码,在这本书里面给出了这部分代码,不过我个人觉得要深入理解的话只看这本书肯定不够。不过还好我们学过所以这里再回顾一遍理解起来并不很难。
而要去做一个Linux文件树模拟相当于写一个基于二叉树数据结构的编程项目,里面的一些操作如mkdir、rmdir、creat、rm、cd、pwd、ls其实就是一些功能,我们可以编写对应的函数去实现它。
参考了一些博客改了改,虽然很多功能很简陋,最后还是有结果
#include <stdint.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdbool.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <libgen.h>
typedef struct Node{
char name[64];
char type;
struct Node *childPtr, *siblingPtr, *parentPtr;
}Node;
static Node *root, *cwd;
bool ap = false;
int deep = 0;
int num = 0;
static char line[128];
char command[16], pathname[64];
char dname[64], bname[64];
int mkdir_(),rmdir_(),cd(), ls(),pwd(),creat_(),rm(),save(),reload(),menu(),quit();
char *commad_list[] = {"mkdir", "rmdir", "cd", "ls", "pwd", "creat", "rm", "save", "reload", "menu", "quit", NULL};
int (*f_ptr[])() = { (int (*)())mkdir_, rmdir_, cd, ls, pwd, creat_, rm, save, reload, menu, quit };
void get_dir_base(){
char temp[128];
ap = false;
if(pathname[0] == '/') ap = true;
strcpy(temp, pathname);
strcpy(dname, dirname(temp));
strcpy(temp, pathname);
strcpy(bname, basename(temp));
}
void initialie(){
root = (Node*)malloc(sizeof(Node));
root->siblingPtr = root;
root->parentPtr = root;
root->childPtr = NULL;
root->name[0] = '/';
root->name[1] = '\0';
root->type = 'D';
cwd = root;
}
int findCommand(){
int i = 0;
while(commad_list[i]){
if(!strcmp(command, commad_list[i])) return i;
i++;
}
return -1;
}
void getCommand(){
bzero(line, 128);
read(STDIN_FILENO, line, 128);
bzero(command, 128);
line[strlen(line) - 1] = '\0';//kill \n
sscanf(line, "%s %s", command, pathname);
int idx = findCommand();
if(idx == -1) return;
f_ptr[idx]();
}
Node* mk_Node(char type){
Node *p = (Node*)malloc(sizeof(Node));
strcpy(p->name, bname);
p->type = type;
p->childPtr = NULL;
p->siblingPtr = NULL;
return p;
}
Node* find_d(){
get_dir_base();
char *s;
Node* per ;
per = cwd;
if(ap) per = root;
Node* cur = per->childPtr;
if(strcmp(dname, ".")){
s = strtok(dname, "/");
while(s){
while(cur){
if(!strcmp(s, cur->name) && cur->type == 'D'){
per = cur;
break;
};
cur = cur->siblingPtr;
}
if(cur == NULL) return NULL;
cur = per->childPtr;
s = strtok(NULL, "/");
}
}
return per;
}
int _pwd(Node* p, int fd){
char *s[deep];
char tmp[1024];
bzero(tmp, 1024);
Node* cur = p;
int i = 0;
int off = 0;
for(;i < deep && cur != root; i++){
s[i] = cur->name;
cur = cur->parentPtr;
}
tmp[off++] = '/';
while(i--){
int size = strlen(s[i]);
memcpy(tmp + off, s[i], size);
off += size;
if(i) tmp[off++] = '/';
}
tmp[off++] = '\n';
tmp[off] = '\0';
write(fd, tmp, strlen(tmp));
return 0;
}
int pwd(){
return _pwd(cwd, STDOUT_FILENO);
}
int save(){
int fd = open(pathname, O_RDWR|O_CREAT|O_TRUNC, 0666);
Node *stack[num];
Node* cur = root->childPtr;
if(cur == NULL) return -1;
int i = 0;
stack[i++] = cur;
while(i > 0){
cur = stack[--i];
write(fd, &cur->type, 1);
write(fd, " ", 2);
_pwd(cur, fd);
if(cur->siblingPtr) stack[i++] = cur->siblingPtr;
if(cur->childPtr) stack[i++] = cur->childPtr;
}
close(fd);
return 0;
}
int cd(){
Node* per = find_d();
Node* cur = per->childPtr;
while(cur){
if(!strcmp(bname, cur->name) && cur->type == 'D') break;
cur = cur->siblingPtr;
}
if(cur == NULL) return -1;
cwd = cur;
return 0;
}
int _rmdf(char type){
Node* per = find_d();
Node* cur = per->childPtr;
while(cur){
if(!strcmp(bname, cur->name) && cur->type == type) break;
per = cur;
cur = cur->siblingPtr;
}
if(cur == NULL) return -1;
if(type == 'D' && cur->childPtr != NULL) return -1;
if(per->childPtr == cur){
free(cur);
per->childPtr = NULL;
}else{
per->siblingPtr = cur->siblingPtr;
free(cur);
}
return 0;
}
int ls(){
Node* per = find_d();
if(per == NULL) return -1;
Node* cur = per->childPtr;
while(cur){
printf("type : %c name : %s \n", cur->type, cur->name);
cur = cur->siblingPtr;
}
return 0;
}
int rmdir_(){
if(!_rmdf('D')){
deep--;
return 0;
}
return -1;
}
int rm(){
return _rmdf('F');
}
int _mkdf(char type){
Node* per = find_d();
if(per == NULL) return -1;
Node* cur = per->childPtr;
if(cur == NULL){
per->childPtr = mk_Node(type);
per->childPtr->parentPtr = per;
num++;
return 0;
}
while(cur){
if(!strcmp(bname, cur->name) && cur->type == 'D') return -1;
per = cur;
cur = cur->siblingPtr;
}
per->siblingPtr = mk_Node(type);
per->siblingPtr->parentPtr = per->parentPtr;
num++;
return 0;
}
int reload(){
struct stat st;
if(stat(pathname, &st) == -1) return -1;
int fd = open(pathname, O_RDONLY);
char buf[st.st_size];
char tmp[128];
char type;
int i = 0;
int off = 0;
while((i = read(fd, buf + off, st.st_size - off)) > 0){
off += i;
}
close(fd);
i = 0;
int j = 0;
while(i < off){
if(buf[i] != '\n') i++;
else{
bzero(tmp, 128);
memcpy(tmp, buf + j, i - j);
sscanf(tmp, "%c %s",&type , pathname);
if(type == 'D') mkdir_();
else if(type == 'F') creat_();
else return -1;
j = ++i;
}
}
return 0;
}
int menu(){
printf("mkdir dirpath\n");
printf("rmdir dirpath\n");
printf("cd dirpath\n");
printf("ls dirname\n");
printf("pwd \n");
printf("cteat filepath\n");
printf("rm filepath\n");
printf("save filepath\n");
printf("reload filepath\n");
printf("quit\n");
return 0;
}
int creat_(){
return _mkdf('F');
}
int mkdir_(){
if(!_mkdf('D')){
deep++;
return 0;
}
return -1;
}
int quit(){
sprintf(pathname, "text.txt");
save();
exit(0);
return 0;
}
void show(){
printf("welcom to !\n");
printf("command menu show commands\n");
}
int main()
{
//scanf("%*[^\n]%*c");
initialie();
show();
while(1){
//fflush(stdin);
getCommand();
}
return 0;
}
这个是运行的最终结果
通过这个项目,我又重新过手了C语言,看来C语言确实比较底层的语言,编写Linux文件系统原来和我们数据结构的课程实验是如此相像。
因为时间比较紧促,这个项目的细节还需要好生消化一下,这个程序的逻辑本身并不难,就是用树状结构模拟文件系统。尤其是二叉树的遍历等部分的代码,在网上都有现成的,但是自己在已知逻辑的情况下能否靠自己写出来,其实自己尝试以后才知道。
标签:Node,return,cur,读书笔记,int,per,第一章,教材,type From: https://www.cnblogs.com/zhou-huilin/p/16654939.html