操作系统课程作业实现shell

操作系统课程作业实现shell

2023年8月1日发(作者:)

.

实验六 简单shell

一、实验目的

通过实现一个简单的shell命令解释器,加深对Windows系统调用的理解,同时了解管道命令的设计实现机制。

二、实验内容

在Windows平台上实现Linux的某些命令:

展开指定目录:myls [dir]

文件查看和连接:mycat [-n] [file1 file2……]

显示文档的开头:myhead number filename

排序:mysort [file1 file2 ……]

统计可执行文件执行时间:mytime

复制文件:mycp a b

自定义出错提示命令:mysl

分别对应于Linux系统中的ls cat head sort time cp sl命令,在具体设计实现的时候用法做了简化。其中,mycat、myhead和mysort为三条管道命令,例如:

mysort | myhead 6 | mycat -n

三、程序设计与实现

1、程序中使用的结构体

(1) 处理输入命令字符串的自定义结构体CMD,定义如下

struct CMD{

int cmdposi[10];//记录每条管道命令在命令字符串中是第几个字符串

int lastcmdposi; //最后一条命令是第几条命令

int subcmdparameter[10];//每条管道命令所带的参数个数

char* subcmd[10][10];//记录命令行中的每一个字符串

}cmd;

实用文档. .

对于输入命令:mysort | myhead 6 | mycat –n

字符串的位置: 0 1 2 3 4 5 6 7 8

cmdposi[]:

subcmd[][] subcmd[1]: myhead 6

subcmd[2]: mycat –n

(2) 创立进程时,PROCESS_INFORMATION结构返回有关新进程及其主线程的信息。其结构定义如下:

typedef struct _PROCESS_INFORMATION {

HANDLE hProcess;

HANDLE hThread;

//新创立进程的句柄

//新创立进程的主线程的句柄

//新创立进程的标识

//新创立进程的主线程的标识

cmdposi[0]=0 cmdposi[1]=4 cmdposi[2]=7

DWORD dwProcessId;

DWORD dwThreadId;

} PROCESS_INFORMATION, *LPPROCESS_INFORMATION;

(3) 获取系统时间时,使用到SYSTEMTIME结构体,其定义如下:

typedef struct _SYSTEMTIME {

WORD wYear;//年

WORD wMonth;//月

WORD wDayOfWeek;//星期

WORD wDay;//天

WORD wHour;//小时

WORD wMinute;//分钟

WORD wSecond;//秒

WORD wMilliseconds;//毫秒

} SYSTEMTIME, *PSYSTEMTIME;

(4) 在调用函数FindFirstFile、FindNextFile时,将找到的文件的信息存储在实用文档. .

win32_FIND_DATA结构体中。

typedef struct _WIN32_FIND_DATA {

DWORD dwFileAttributes;//文件属性

FILETIME ftCreationTime;//文件创立时间

FILETIME ftLastAccessTime;//文件最后一次访问时间

FILETIME ftLastWriteTime;//文件最后一次修改时间

DWORD nFileSizeHigh;//文件长度高32位

DWORD nFileSizeLow;//文件长度低32位

DWORD dwReserved0;//系统保存

DWORD dwReserved1;//系统保存

TCHAR cFileName[MAX_PATH];//长文件名

TCHAR cAlternateFileName[14];

} WIN32_FIND_DATA, *PWIN32_FIND_DATA, *LPWIN32_FIND_DATA;

(5) FILETIME结构体用来记录文件时间,该结构是表示100纳秒间隔数为64位值从1601年一月1日。

typedef struct _FILETIME {

DWORD dwLowDateTime;//低32位

DWORD dwHighDateTime;//高32位

} FILETIME, *PFILETIME, *LPFILETIME;

2、使用的主要Windows API

(1) CreateNamedPipe()函数

作用:创立命名管道

调用格式:

HANDLE WINAPI CreateNamedPipe(

LPCTSTR lpName,//管道名称

DWORD dwOpenMode,//管道翻开方式

DWORD dwPipeMode,//管道数据组织方式

DWORD nMaxInstances,//管道最大实例量

DWORD nOutBufferSize,//输出缓冲区长度

实用文档. .

DWORD nInBufferSize,//输入缓冲区长度

DWORD nDefaultTimeOut,//默认等待时间

LPSECURITY_ATTRIBUTES lpSecurityAttributes//平安属性

);

说明:调用成功返回管道的句柄,失败返回无效句柄值。

(2) ConnectNamedPipe()函数

作用:管道创立者与客户进行连接

调用格式:

BOOL WINAPI ConnectNamedPipe(

HANDLE hNamedPipe,//命名管道的句柄

LPOVERLAPPED lpOverlapped//重叠属性

);

说明:如果lpOverlapped为NULL,那么,假设管道已经连接,就返回TRUE,假设发生错误,或者管道已经连接,就返回0;如果lpOverlapped有效,就返回零。

(3) WaitNamedPipe()函数

作用:客户等候与管道创立者建立连接

调用格式:

BOOL WINAPI WaitNamedPipe(

LPCTSTR lpNamedPipeName,//将要连接的管道名称

DWORD nTimeOut//等待时间,以秒为单位

);

说明:当管道创立者已调用ConnectNamePipe()函数等待与一个客户连接时,该函数成功返回非0,如果失败,或者管道不存在,那么返回0。

(4) DisconnectNamedPipe()函数

作用:管道创立者断开与一个客户连接的命名管道

调用格式:

BOOL WINAPI DisconnectNamedPipe(_In_ HANDLE hNamedPipe);

实用文档. .

说明:hNamedPipe为要断开管道的句柄,非0表示成功,0表示失败。

(5)其他的一些API已经在之前的四个实验中详细介绍过,故在此不做赘述。有CreateProcess()、WaitForSingleObject()、GetSystemTime()、FindFirstFile()、FindNextFile()、CreateFile()、ReadFile()、WriteFile()、GetFileTime()、SetFileTime()、CreateDirectory()等。

四、实验结果及分析

1、实验结果

图一 双管道命令

如上图一所示,首先显示的内容,myhead命令取前3行输出,mysort命令那么是将那3行内容排序后再输出,接着mycat命令最后加上行标再次输出。

实用文档. .

图二 单管道命令

如上图二所示,首先显示的内容,mysort命令将其排序后输出,接着myhead命令取其前3行输出。

图三 mytime命令、myls命令

如上图三所示,mytime命令可以计算可执行程序的执行时间,myls命令可以展开指定目录,并显示文件名称、创立时间、修改时间和文件大小。

实用文档. .

图四 mycp命令

如上图四所示,mycp命令其实就是实验五的任务,在将其收录进我们shell中。

图五 mysl命令

如上图五所示,在Linux系统中,ls命令是很常用的,所以程序员一旦不小心敲错了,误写成sl也是情有可原的,这时Linux的terminal会显示一辆跑动的小火车,以此提示程序员。上述mysl命令的设计也是基于此,这也使得我们的shell变得生动有趣。

实用文档. .

2、实验分析

(1)UNIX系统进程之间的通信方式简介以及比照

管道通信:

管道通信允许进程之间按照FIFO方式进行传输数据,一些进程用write命令向管道写入数据,另一些进程用read命令从管道中读数据,且彼此之间同步执行,管道通信是进程使用文件系统中文件进行的,只要通信的双方根本同步,可认为这个文件是无限大的。

无名管道是一个临时文件,当文件被关闭后,文件就不复存在了,它是提供应同族进程之间使用的通信方法。

有名管道那么是实现了无家族关系进程之间的通信,任何知道管道名字的进程都可以翻开使用,有名管道文件一旦被创立后,磁盘上有一个对应的目录项和索引节点,它与普通文件类似,是通过路径名存取的,只要这种文件不显示删除,它就永远存在,只是文件长度为零。

信号量:

信号量机制的功能是比拟强的,它提供了信号量集合。这种机制是通过P、V操作原语实现的,每次只进行单位数据的交互,通信效率比拟低。

消息缓冲&共享内存:

采用消息缓冲和共享内存区是,进程之间可以进行大批数据的交互。

其中,信号量、消息缓冲和共享内存区都属于UNIX系统V的交互进程通信使用的资源,又成IPC资源。IPC属鸡的数据结构在进程之间请求IPC资源〔信号量、消息队列和共享内存区〕

(2)Windows对于管道通信的设计机制简介

Windows最大的特点是建立一个简单的客户机/效劳器程序设计体系,在这个体系结构中,在客户机与效劳器之间,数据既可以单向传递,也可双向流动。对命令管道效劳器和客户机来说,两者的区别在于:效劳器是唯一一个有权创立命名管道的进程,也只有它才能接受管道客户机的连接请求。对一个客户机应用来说,它只能同一个现成的命名管道效劳器建立连接。在客户机应用和效劳器应用之间,一旦建立好连接,两个进程都能使用标准的Win32函数,在管道上进行数据的读取与写入。

实用文档. .

(3)客户与效劳器〔管道创立者〕之间使用管道通信的步骤:

a.建立连接,效劳端通过函数CreateNamedPipe()创立一个命名管道的例如并返回用于今后操作的句柄。客户端通过调用WaitNamedPipe()使效劳进程等待来自客户的例如连接,如果在超时值变为0之前,有一个管道可以为连接使用,那么WaitNamePipe()将返回TRUE,并通过CreateFile()来呼叫效劳端的连接。此时效劳端将接受客户端的连接请求,成功建立连接,效劳端ConnectNamedPipe()返回TRUE,客户端CreateFile()将返回一指向管道文件的句柄。

b.通信实现,连接建立之后,客户端与效劳器使用得到的管道文件句柄通过调用WriteFile()和ReadFile(),彼此之间进行信息交换。

c.连接终止,当客户端与效劳端的通信结束,客户端调用CloseHandle()断开连接;而效劳端接着调用DisconnectNamedPipe()。

(4)实验设计简介

对于管道命令,本shell中只有myhead、mycat、mysort这三个管道命令。

相互协作可以实现管道(当然,这三个命令也可以单独使用),就拿myhead来说,它的调用形式只有如下四种形式:

**** | myhead 3 | ****

**** | myhead 3

myhead 3 | ****

对于此,设置两个标记 LeftMycatPipeFlag、LeftMysortPipeFlag 用来记录myhead命令前面是否出现了管道,同时设置标记 islastcmd 来记录 myhead 是否为最后一条子命令。这样就可以通过这三个标志来分情况处理。具体设计实现见代码:

实用文档. .

LeftMycatPipeFlag = find_left_mycat_pipe();

LeftMysortPipeFlag = find_left_mysort_pipe();

int existence = LeftMycatPipeFlag || LeftMysortPipeFlag;

}

else if(existence==1 && islastcmd==1){ //eg: mycat -n | myhead 3

}

else if(existence==0 && islastcmd==0){ //eg: myhead 3 | mycat -n

}

//process data ,put into buffer

process_file_into_buffer();

//create pipe, place data

create_myhead_pipe();

//get data from pipe,then print

if(LeftMycatPipeFlag == 1)

get_left_mycat_pipe();

//process data ,then print

process_file_print();

// Judge the existence of left pipe

if(existence==1 && islastcmd==0){ //eg: mycat -n | myhead 3 | mysort

}

//get data from pipe

if(LeftMycatPipeFlag == 1)

get_left_mycat_pipe();

else if(LeftMysortPipeFlag == 1)

get_left_mysort_pipe();

//create pipe, place data

create_myhead_pipe();

else if(LeftMysortPipeFlag == 1)

get_left_mysort_pipe();

对于命令“ | myhead 6 | mycat –n〞来说:

管道通信原理图如下所示:

实用文档. .

(5)shell各局部说明

---------------------- shell 主控程序 400行 -------------------------

对输入的命令进行处理,为各个命令创立进程,运行结束时终止进程

---------------------- myls 展开指定目录 200行 ----------------------

使用格式:myls [dir]

参数说明:假设dir不为“.〞,那么展开dir,否那么展开当前目录,显示文件创立时间、最后修改时间、大小等根本属性

------------------ mycat 文本文件查看和连接 400行 -------------------

使用格式:mycat [-n] [file1 file2 ……]

参数说明:由1开始对所有输出的行数编号: -n

-------------------- myhead 显示文档的开头 300行 --------------------

使用格式:myhead number filename

参数说明:number为要显示的行数

------------------------ mysort 排序 300行 --------------------------

使用格式:mysort [file1 file2 ……]

参数说明:命令用于将所有文件内容按第一列来排序(假设第一列相同,那么看第二列……)

----------------------- mycp 复制文件 150行 ------------------------

使用格式:mycp sour_dir des_dir

参数说明:sour_dir为源目录,des_dir为新创立的目录。

--------------- mytime 统计可执行文件执行时间 100行 -----------------

参数说明:参数为一个可执行文件

------------------------ mysl 错误提示 400行 -----------------------

实用文档. .

使用格式:mysl

说明:在Linux系统中,ls命令是很常用的,经常会误写成sl,这时shell会显示一辆跑动的小火车,以此提示程序员。

命令举例:

〔1〕mycat -n file1 | myhead 3 将文件 file1 的内容加上行号后显示前3行

〔2〕myhead 3 file1 | mycat -n 将文件 file1 的内容显示前3行后加上行号

〔注:上述两条命令的操作最终结果等价〕

〔3〕mysort file1 file2 | mycat -n | myhead 5 将 file1 和 file2 的内容合并排序、加上行号、显示前5行

〔4〕mycat -n

〔5〕myhead 2 2行

〔6〕 把

〔7〕mytime 显示

〔8〕mycp a b 将文件夹a的内容复制到文件夹b

〔9〕myls . 展开当前目录

〔10〕myls a 展开指定目录a

〔11〕mysl 显示跑动的小火车

五、实验收获与体会

实验代码量到达了2300行,实现了简单shell,根本到达了要求。不过由于时间关系实验中还有很多缺陷和缺乏有待改良。

谈兆年的心得体会:

本次试验中,我负责shell整体框架的设计以及myhead、mysort、mycat三条管道命令的实现。在实验中,学习了很多关于UNIX和Windows的管道设计机制相关知识。

实验中最大的挑战莫过于对于管道命令的各个字符串的处理,为此我特地设计了CMD结构体来分割管道命令,还有实验中需要处理诸多函数,函数相互调用的时候,接口设计十分重要。光是某些单个命令的设计实现就需要400行的代码量,可想而知,假设是接口设计的不好,势必会给后续工作带来影响。尤其是对于命令所带的参数的处理尤其麻烦,还有为每个命令创立进程的时候,szCmdline实用文档. .

所带的参数也是需要小心处理。

本次实验无疑极大地提升了我的工程动手能力,一个星期持续不断的编程还是非常刺激的,这其中遇到的问题胜不胜数,比方说,效劳器端创立pipe时,buffer缓冲区不能为空:假设为空,那么在创立pipe时调用的WriteFile()没有在pipe中写入任何数据,然后客户端和pipe建立连接后,企图调用ReadFile()获取pipe中的数据时,发现里面什么都没有,那么ReadFile()调用失败。

徐欣廷的心得体会:

在这次大作业中,我负责完成myls命令,由于代码的实现过程比拟简单,在Windows下就是对目录的读取过程,需要自己添加的内容就是对内容的转义。比方说文件日期,从FILETIME转换成可读的本地时间、文件长度的高32位和低32位的拼接等操作。

因为涉及到的进程数量比拟多,所以在子进程的代码中,需要注意的就是对内存的控制,每次new操作的内存空间在不需要的时候要及时释放,否那么会导致内存泄漏,产生严重的后果;其次,对于翻开的文件句柄,需要及时关闭,否那么经过屡次调用之后可能会导致系统调用出现异常。经过本次实验,我掌握了很多操作系统方面的知识,在之后的实际应用中会带来极大的帮助。

杨知水的心得体会:

在这最后一次课程设计中,在我们小组共同努力下完成了一个shell命令解释器的根本功能。我们在这次综合了几乎所有实验课知识的实验中收益颇丰,不仅稳固了之前几次实验的知识,而且学到了不少新的知识,比方对于管道通信的理解等等。

对于整个实践课程,不仅培养了我们独立思考、动手操作以及合作编程的能力,在各种其它能力上也都有了提高。更重要的是,在实验课上,我们学会了很多学习的方法。课程设计使我懂得了理论与实际相结合是很重要的,只有理论知识是远远不够的,只有把所学的理论知识与实践相结合起来,才能真正的掌握知识。

杜田野的心得体会:

首先感谢小组成员的大力帮助,才能这样顺利的完成任务。在其中,我自己也是锻炼了自我的学习能力,当然还学会了及时与同学交流,向老师请教。我认识到了,学习其实是一个循序渐进的过程,但同时也是一个突飞猛进的过程,有的时候会卡在哪一个结点,等待一段时间搞定就会有很大收获。

实用文档. .

刚开始时候看了课本,但是因为这一次的实验算是自己构思,必须提前构思好,要不然编起来会思路不清。

实用文档.

发布者:admin,转转请注明出处:http://www.yc00.com/web/1690875556a452511.html

相关推荐

发表回复

评论列表(0条)

  • 暂无评论

联系我们

400-800-8888

在线咨询: QQ交谈

邮件:admin@example.com

工作时间:周一至周五,9:30-18:30,节假日休息

关注微信