操作系统大作业——基于OrangeOS的改写(2)

发布时间:2024年01月23日

武汉大学21级网安操作系统大作业 任务二

?1. 问题介绍与思路摘要

?1.1问题介绍

?本任务是 OS 期末实验的第二个基本任务,主要目标是理清代码结构并扩展shell,提供一些自制的指令方便后续实验。具体要求如下:

??利用当前OrangeS所提供的系统调用和API,编写2个以上可执行程序(功能自定),并编译生成存储在文件系统中

??分析教材的Shell代码,画出Shell的流程图,在Shell中调入你所编写的可执行程序,启动并执行进程,注意使用教材中所提供的系统调用来实现

??进程结束后返回Shell

?1.2 解决方案摘要

在chapter11/a的基础上,我首先实现了一个基本的测试指令test,其可以输出我的学号姓名;然后实现了linux系统中常见的文件操作指令touch(创造文件)和rm(删除文件),以及常用的目录操作指令ls(显示当前目录下的所有文件),又改写了echo指令(写入内容到文件),添加了cat指令(查看文件内容)、cp指令(复制文件)。

?2.?具体思路及其实现

这部分实验原理实际上是较为简单的。在第十章书上实现了“简单的shell”过后,oranges操作系统已经具备了将command中的.c文件根据makefile的指令进行编译、链接、并装载入操作系统中的能力。之后,在bochs中输入命令名称就可以调用这些指令了。 因此,要扩展shell,编写更多的可执行指令,只需要在command 中编写其代码即可。?

下面是教材中shell的流程图:?(不知道为啥这么糊)

?

接着,我将讨论我实现的指令及其思路。?

?2.1 test指令

?test指令是我用来测试的,显而易见,就是输出我的姓名拼音和学号。

int?main(int?argc,?char?*?argv[])??
{??
????printf("hello,?my?name?is:\n");??
????printf("***?\n");??
????printf("My?student?number?is:\n");??
????printf("***\n");??
????return?0;??
}??

然后,在Makefile中添加编译信息,仿照着其他指令来:?

test.o:?test.c?../include/type.h?../include/stdio.h??
????$(CC)?$(CFLAGS)?-o?$@?$<??
??
test?:?test.o?start.o?$(LIB)??
????$(LD)?$(LDFLAGS)?-o?$@?$???
BIN?????=?echo?pwd?test

先在根目录make image,再进入command文件夹make、make install,最后退出来make image。?

摁住shift+F2后输入test,得到如下:?

?

?所以test指令添加成功。

?2.2 torch和rm指令

?实现linux系统中常见的文件操作指令touch(创造文件)和rm(删除文件)。这部分的主要思路是利用open和unlink两个函数。

?touch.c
利用了调用open(file_name, O_CREAT) 时,若file_name不存在则会自动创建的特征。使用~判断返回值是否为-1(出错)。如果出错则报错并提示用户,最常见的可能性是在尝试创建已经存在的文件。

#include?"stdio.h"??
??
int?main(int?args,?char?*?argv[])??
{??
????if(~open(argv[1],?O_CREAT))?{??
????????printf("Successfully?created?%s.\n",?argv[1]);??
????}else?{??
????????printf("Failed?to?create?%s,?maybe?because?this?file?has?been?existed.\n",argv[1]);??
????}??
????return?0;??
}??

rm.c?
rm指令的实现方式类似,采用的是unlink函数,unlink从文件系统中中删除一个名字,若这个名字是指向这个文件的最后一个链接,并且没有进程处于打开这个文件的状态,则删除这个文件,释放这个文件占用的空间。调用成功返回0,不成功返回-1。

#include?"stdio.h"??
??
int?main(int?args,?char?*?argv[])??
{??
????if?(~unlink(argv[1]))?{??
????????printf("rm?file?failed,?please?try?again?later?or?check?your?authority.\n");??
????????return?-1;??
????}??
????printf("Successfully?removed?file?%s\n",?argv[1]);??
????return?0;??
}??

编写完上述指令后,在Makefile文件中修改相应指令,然后再make、make install、make image将可执行文件装载入软盘即可在bochs中执行,步骤和上述一样。执行结果如下:?

现在的验证看起来还较为苍白和没有说服力,之后我们将使用ls指令来验证我们是否真的创建和删除了这些文件。?

?2.3 ls指令

?接下来实现的是linux文件系统中另一个常用的指令ls,调用其将打印出文件系统中,当前路径下的所有文件的名称。真正的ls还带有不少其余功能,比如用参数-a显示隐藏文件,-d显示目录名等等,这里我们只实现最简单的、原始的功能。

ls指令的实现思路如下:在fs/misc.c中我们已经有了实现的search_file,所以我们可以依葫芦画瓢,模拟一个进程间的通信,仿造原有的search_file指令写一个search_dir函数,在本目录下扫描文件名并写入一条message中,之后返回这个message即可。?

具体来说,我们首先在fs文件夹下的main.c中添加ls的case具体为首先找到 task_fs,然后添加如下case:?

case?SEARCH:??
    fs_msg.BUF?=?do_search_dir();

?下面的switch(msgtype)也要添加case:

case?SEARCH:

?意思是之后的message type如果是search就调用search_dir函数,并将返回的结果存储在缓冲区BUF中。

?添加完这部分调用过后,我们需要在lib文件夹下添加一个发消息的接口。接下来,我们模仿lib文件夹下的代码,写一个发消息的文件

search_dir.c?

#include?"type.h"??
#include?"stdio.h"??
#include?"const.h"??
#include?"protect.h"??
#include?"string.h"??
#include?"fs.h"??
#include?"proc.h"??
#include?"tty.h"??
#include?"console.h"??
#include?"global.h"??
#include?"keyboard.h"??
#include?"proto.h"??
??
PUBLIC?char*?search_dir(char*?path)?{??
????MESSAGE?msg;??
????msg.type?=?SEARCH;??
????memcpy(msg.pBUF,?path,?strlen(path));??
????//?printl("msg.pBug?address?is?%d\n",?msg.pBUF);??
????//?printl("BUF?:?%s\n",?msg.pBUF);??
????send_recv(BOTH,?TASK_FS,?&msg);??
????return?msg.pBUF;??
}??

此处是给message.type枚举体增加了一个属性SEARCH,用来之后确认是否是ls指令。?

?然后在type.h中添加一个Path数组,用来存储未来搜索到的结果。Path数组同样需要在message定义处增加定义。

typedef?struct?{??
????int?source;??
????int?type;??
????char?Path[200];??
????union?{??
????????struct?mess1?m1;??
????????struct?mess2?m2;??
????????struct?mess3?m3;??
????}?u;??
}?MESSAGE;??

最后,就是在fs文件夹下的misc.c中写一个do_search_dir()函数,搜索当前路径下的文件,将文件名存储到buf中然后返回。具体来说,我们添加一个函数,基本上就是照着search_file写,只不过最后一步将地址拷贝到缓冲区中:?

?已有的search_file()

PUBLIC?int?search_file(char?*?path)??
{??
????int?i,?j;??
??
????char?filename[MAX_PATH];??
????memset(filename,?0,?MAX_FILENAME_LEN);??
????struct?inode?*?dir_inode;??
????if?(strip_path(filename,?path,?&dir_inode)?!=?0)??
????????return?0;??
??
????if?(filename[0]?==?0)???/*?path:?"/"?*/??
????????return?dir_inode->i_num;??
??
????/**??
?????*?Search?the?dir?for?the?file.??
?????*/??
????int?dir_blk0_nr?=?dir_inode->i_start_sect;??
????int?nr_dir_blks?=?(dir_inode->i_size?+?SECTOR_SIZE?-?1)?/?SECTOR_SIZE;??
????int?nr_dir_entries?=??
??????dir_inode->i_size?/?DIR_ENTRY_SIZE;?/**??
???????????????????????????*?including?unused?slots??
???????????????????????????*?(the?file?has?been?deleted??
???????????????????????????*?but?the?slot?is?still?there)??
???????????????????????????*/??
????int?m?=?0;??
????struct?dir_entry?*?pde;??
????for?(i?=?0;?i?<?nr_dir_blks;?i++)?{??
????????RD_SECT(dir_inode->i_dev,?dir_blk0_nr?+?i);??
????????pde?=?(struct?dir_entry?*)fsbuf;??
????????for?(j?=?0;?j?<?SECTOR_SIZE?/?DIR_ENTRY_SIZE;?j++,pde++)?{??
????????????if?(memcmp(filename,?pde->name,?MAX_FILENAME_LEN)?==?0)??
????????????????return?pde->inode_nr;??
????????????if?(++m?>?nr_dir_entries)??
????????????????break;??
????????}??
????????if?(m?>?nr_dir_entries)?/*?all?entries?have?been?iterated?*/??
????????????break;??
????}??
??
????/*?file?not?found?*/??
????return?0;??
}??

添加的do_search_dir()?

PUBLIC?int?do_search_dir()?{??
????char*?dir?=?fs_msg.Path;??
????int?pointer?=?0;??
??
????int?i,?j;??
??
????char?filename[MAX_PATH];??
????memset(filename,?0,?MAX_FILENAME_LEN);??
????struct?inode*?dir_inode;??
????if?(strip_path(filename,?dir,?&dir_inode))?{??
????????return?0;??
????}??
??
????//?if?(filename[0]?==?0)?{??
????//?return?dir_inode->i_num;??
????//?}??
????/**??
?????*?Search?the?dir?for?the?file.??
?????*/??
????int?dir_blk0_nr?=?dir_inode->i_start_sect;??
????int?nr_dir_blks?=?(dir_inode->i_size?+?SECTOR_SIZE?-?1)?/?SECTOR_SIZE;??
????int?nr_dir_entries?=?dir_inode->i_size?/?DIR_ENTRY_SIZE;?/**??
?????????????????????????????????????*?including?unused?slots??
?????????????????????????????????????*?(the?file?has?been?deleted??
???????????????????????????????????? *?but?the?slot?is?still?there)??
????????????????????????????????????????????????????????????*/??
????struct?dir_entry*?pde;??
????for?(i?=?0;?i?<?nr_dir_blks;?i++)?{??
????????RD_SECT(dir_inode->i_dev,?dir_blk0_nr?+?i);??
????????pde?=?(struct?dir_entry*)fsbuf;??
??
????????for?(j?=?0;?j?<?SECTOR_SIZE?/?DIR_ENTRY_SIZE;?j++,?pde++)?{??
????????????dir[pointer]?=?'?';??
????????pointer?+=?1;??
????????memcpy(dir?+?pointer,?pde->name,?strlen(pde->name));??
????????pointer?+=?strlen(pde->name);??
????????}??
????}??
????return?(void*)0;??
?}??

然后在proto.h中声明,?Makefile中也添加相应的

完成过后,就可以在函数里调用 search_dir() 这个函数来获取地址了。之后我们只需要在 command文件夹下编写这部分代码即可。?

ls.c?

#include?"stdio.h"??
#include?"const.h"??
#include?"string.h"??
#include?"fs.h"??
??
int?main?(int?args,?char*?argv[])??
{??
????char*?result;??
????result?=?search_dir("/");??
????printf("%s\n",?result);??
????return?0;??
}??

?编译运行

最后测试是否能成功调用命令。有了 ls 命令,我们还能一并测试刚刚写的 touch、rm 命令是否真的成功创建、删除了文件。我们测试的结果如下:?

?

可以看到,在touch后ls,确实出现了刚刚我们创建的文件。同理,rm也确实能删除文件,这说明我们创建的这三个命令都是正常工作的。?

?2.4 echo指令

原有的echo指令功能有些简陋,只能够实现打印出所有通过命令行传入的参数,比如echo 123,就打印出“123”。我将其功能进行了改写,即通过命令行传入的文本参数写入到指定的文件中,比如echo dp dp.txt,就能够将dp写入dp.txt文件中。?

?改写的echo思路如下:

先看命令格式是否正确,即echo <text> <filename>。正确就调用open打开或创建文件,然后调用write写入文件,最后关闭文件。思路很简单,代码如下:

echo.c?

#include?"stdio.h"??
#include?"string.h"??
??
int?main(int?argc,?char?*argv[])?{??
????if?(argc?<?3)?{??
????????printf("Usage:?echo?<text>?<filename>\n");??
????????return?1;??
????}??
??
????//?打开或创建文件以写入数据??
????int?fd?=?open(argv[argc?-?1],?O_RDWR?|?O_CREAT);??
????if?(fd?<?0)?{??
????????printf("Failed?to?open?or?create?%s\n",?argv[argc?-?1]);??
????????return?1;??
????}??
??
????int?i;??
????//?遍历所有参数并写入文件,除了最后一个参数(文件名)??
????for?(i?=?1;?i?<?argc?-?1;?i++)?{??
????????write(fd,?argv[i],?strlen(argv[i]));??
????????if?(i?<?argc?-?2)?{??
????????????write(fd,?"?",?1);??//?在单词之间添加空格??
????????}??
????}??
????write(fd,?"\n",?1);??//?添加换行符??
??
????//?关闭文件??
????close(fd);??
????return?0;??
}??

因为是改写,Makefile中已经存在,所以不必修改。编译执行效果如下:?

显然,生成了一个dp.txt文件,但是我们不知道hello,dp是否写入该文件,所以我又添加了一条指令,即cat指令,来帮助我们验证。?

?2.5 cat指令

cat代码基本思路如下:

先看命令格式是否正确,即cat <filename>。正确就打开文件,然后读取并输出,最后关闭文件。代码如下:

cat.c?

#include?"stdio.h"??
#include?"string.h"??
??
#define?BUFFER_SIZE?256??
??
int?main(int?argc,?char?*argv[])?{??
????if?(argc?!=?2)?{??
????????printf("Usage:?cat?<filename>\n");??
????????return?1;??
????}??
??
????//?打开文件??
????int?fd?=?open(argv[1],?O_RDWR);??
????if?(fd?<?0)?{??
????????printf("Failed?to?open?%s\n",?argv[1]);??
????????return?1;??
????}??
??
????char?buf[BUFFER_SIZE];??
????int?n;??
??
????//读取并输出???
????while?((n?=?read(fd,?buf,?BUFFER_SIZE))?>?0)?{??
????????write(1,?buf,?n);??//?1?是标准输出文件描述符??
????}??
??
????//?关闭文件??
????close(fd);??
????return?0;??
}??

Makefile改写和编译过程跟之前一样,最终代码效果如下:?

?可以看到文件内容,成功。

?2.6 cp指令

做完cat指令,我仔细思考,发现还可以加一个指令即,cp指令。因为思路是一脉相承的。?

我的思路是这样的:

依旧是先看命令行格式是否正确,即cp <source> <destination>,source是源文件,destination是复制到的文件。然后分别打开源文件和目标文件,目标文件若不存在就创建。之后,复制文件内容,程序使用 read 函数从源文件读取数据到缓冲区,并用 write 函数将数据写入目标文件,这个过程持续进行,直到源文件的所有内容都被读取和写入。最后关闭文件。

cp.c?

#include?"stdio.h"??
#include?"string.h"??
??
#define?BUFFER_SIZE?256??
??
int?main(int?argc,?char*?argv[])?{??
????if?(argc?!=?3)?{??
????????printf("Usage:?cp?<source>?<destination>\n");??
????????return?1;??
????}??
??
????//?尝试以打开源文件??
????int?src_fd?=?open(argv[1],?O_RDWR);??
????if?(src_fd?<?0)?{??
????????printf("Failed?to?open?source?file?%s\n",?argv[1]);??
????????return?1;??
????}??
??
????//?尝试打开目标文件,如果不存在则创建??
????int?dest_fd?=?open(argv[2],?O_RDWR?|?O_CREAT);??
????if?(dest_fd?<?0)?{??
????????printf("Failed?to?open?or?create?destination?file?%s\n",?argv[2]);??
????????close(src_fd);??
????????return?1;??
????}??
??
????char?buf[BUFFER_SIZE];??
????int?n;??
??
????while?((n?=?read(src_fd,?buf,?BUFFER_SIZE))?>?0)?{??
????????if?(write(dest_fd,?buf,?n)?!=?n)?{??
????????????printf("Failed?to?write?to?destination?file\n");??
????????????close(src_fd);??
????????????close(dest_fd);??
????????????return?1;??
????????}??
????}??
??
????close(src_fd);??
????close(dest_fd);??
????return?0;??
}??

Makefile改写和编译过程跟之前一样,最终代码效果如下:?

?

文件复制成功,内容一致,成功。?

?3. 工作局限性和改进方向

?3.1 工作局限性

?尽管我们已经实现了linux文件系统中最核心的几个指令,即touch、rm、ls、echo、cat、cp,也通过充分的实验验证了所提议方法的正确性,但我们的工作仍然并非十全十美,主要的局限性有:

??正如前文所说,我所实现的指令都只实现了其最基本的功能。但是,在真实的linux系统中,命令会有更多的参数以完成其他功能。

??rm指令在删除文件的时候存在一些小问题。具体来说,在上次会话中创建的文件保留了下来,在本次会话中,我想用rm删除的时候却发生了assert报错。但是删除本次会话中创建的文件却不会发送报错,不知道为什么。

??echo功能并不完善比如对于已有的文件dp.txt,echo 123 dp.txt,应当把123继续放到文件中中,而不是输出报错。

尽管如此,我们有理由相信在完成了这些命令的基础上,要补充携带参数的功能或打印帮助信息等并非难事,只需要添加case来判断出参数即可。?

3.2 改进方向?

?根据上面所说的局限性,这部分工作未来可以有以下几个改进方向:

? 为指令添加上解析携带参数的功能;

? 实现更多常用指令,如mkdir、grep等。

文章来源:https://blog.csdn.net/qq_62736920/article/details/135730536
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。