第三次编程实验报告。
一.实验内容:
1.应用unix的fork()等系统调用,编写一个c程序具有以下功能:a)实现shell的基本功能,包括有:打印提示符;接受和分析命令行(滤去无效的空格、tab符号以及换行符等);执行命令(要有出错处理;输入需要wait) c)处理多行命令(分析命令行中的‘具有输入输出重定向以及管道功能2.利用unix的信号,编写一个个数字;在程序运行中,如果捕获一个序。
在编程中,要考虑信号复位情况,使程序能够实现多次被打断多次恢复执行二.实验程序功能及实现说明:(源**请参见1.shell程序说明:功能实现情况:
本次实验除了实现题目要求的功能外,向和管道混合出现,改变工作路径功能。程序说明:
本程序**order_name(const*input)。各个函数功能如下:
char*read_order(char作进一步处理。注意这里返回的是用户输入的原始处理,没有作任何处理。
char**order_name(const分别执行每个命令。同时在这里做一些输入指令检查,例如空语句,或只有“;”的情况。int redirect(char *input)令中不存在重定向赋“为标准输入输出。
有四个char>”和“
exit或者bye退出);’
c语言程序,完成数字sigint信号,则转去执行一段显示当前系统时间程。
文件)还新增添了以下功能:主要函数:char*input),intpipel(char*buffer)实现读取用户指令,并将读取的字符串返回共主程序。
char*input)将以“;”分割的每个命令返回。然后按命令。
实现了重定向并执行用户命令。这里注意的是如果用户指<”,则直接执行用户命令,输入输出没有重定向,默认环境d)应用到100*input)b)处理后台程序(不dup(),pipe()系统调用。
输入输出重定。
buffer),char
intredirect(char
intpipel(char*input)实现了多重管道功能。如果用户**现管道符“|”则按照管。
道符首先将用户指令拆分为不同的命令,每个命令的输入输出通过管道串联在一起,然后执行redirect(char*input)完成每个命令的操作。考虑到管道分割的每个命令中可能存在输入输出重定向(例如:ls–al|wc>>由于按照管道拆分的命令中也可能存在着重定向(一般的来讲,存在管道的情况下只有第一个命令存在输入重定向和最后一个存在输出重定向,中间命令输入输出已经被定向到管道中),因此拆分后的命令必须再次调用redirect(char *input)
以上三个函数构成了和统计,分类作用,详细的介绍。2.信号程序说明:
该程序比较简单,号触发函数中结尾要再次执行该函数。具体实现请参考源文件。三.实验结果:
这里我是在程序运行结果如下:1.shell编程结果:在这里不再赘述,具体实现功能可以参看源**注释部分,已经有了很没有什么太难的地方。
windows下使用。
程序的基本功能,securecrt登陆软件登陆到另外一台补充。
因此在信。linux机器上进行操作。来执行相应功能。
shell还有其他一些函数主要做一些添加,这里只要注意要实现信号复位功能,图示结果为编译执行文件后输入各种不同的命令进行测试情况,如重定向,管道等。最后一个如果输入错误指令(只输入;时会给出出错提示,如图最后一行)检测“cd”命令编程效果:
注:实验中测试了各种情况,也同样包括各种管道,输入输出重定向等,并在新进入的目录中输入命令仍然有效,说明本shell程序的确实现了“cd”转换到其他目录的功能。最后程序退出,又回到原来的界面下。
2.信号程序结果:
执行到100s后程序退出,如下图所示:
四.实验**现的问题及解决方法:
1.在shell编程实验中要注意程序执行后用户可能会执行多步操作,持续时间可能会很长,操作涉及的命令和文件也可能很多(在实际的shell中的确如此),为了保证执行效率,本程序必须要注意及时释放申请的空间。由于每次操作时程序都可能会向系统申请一些空间,要保证shell命令执行完后要将该命令涉及的资源全部释放。否则每一条命令的执行都可能会占用一些空间,长时间下去系统执行效率会降低,内存空间利用率也不高。
2.多进程中对于管道的使用。在最开始设计实现多进程时,我只创建了一个管道,在被管道隔开的每个命令实行中通过不断改变输入输出重定向完成每个命令之间的相互通信。但对于处于中间的命令(如命令“ls–a | sort | more”中的sort命令),如果只创建一个管道,则必须首先将输入重定向到该管道,输出也要重定向到该管道作为下一个命令的输入。
这种情况将会产生很怪异的现象,即一个进程里既把该管道当做输入也作为输出,首先同管道本来。
作用相违背,另外这种情况下我也无法得出结果。每一次执行都是到这一步后程序死掉,可能问题也正在这里。所以后来我将程序修改,首先统计原始命令中存在多少管道,依据此来创建相同数量的管道用于每临两个进程之间的通信。
这样程序一方面结构比较清晰,便于调试,同时也避免了上述中管道既作为输入也作为输出的问题。经过这次实验,我总结出管道的一下基本特点:管道主要用于进程之间相互通信,只用于父子进程或兄弟进程之间,且管道为半双工的,同时管道写端对读端存在依赖性。
即只有在管道读端存在时,向管道写入数据才有意义。因此,一方面要注意及时关闭不需要管道文件,减少占用系统资源,另一方面要注意程序的检查,当还要向管道写内容时不能关闭管道读端,否则此时会出现管道断裂,此时进程会收到sigpipe信号,默认操作将会终止该进程。3.特殊的系统调用函数exec系列。
包括以下函数:
int execl( const char *path, const char *arg, .int execlp( const char *file, const char *arg, .
int execle( const char *path, const char *arg , char* const envp)int execv( const char *path, char *const ar**)
int execve( const char *filename, char *const ar** char *const envp)int execvp( const char *file, char *const ar**)
之所以说它们特殊,是指它们成功调用后,系统会根据参数执行一个可执行文件来代替。
当前进程的执行映像。需要注意的是,该调用并没有生成新的进程,而是在原有进程的基础上,替换原有进程的正文,调用前后是同一个进程,进程号pid不变。但执行的程序变了(执行的指令序列改变了)。
因此exec系列函数执行成功后不会返回值(因为进程的执行映像已经被替换,没有接收返回值的地方了)。此时调用进程的实体,包括**段,数据段,和堆栈等都被新的内容取代。只有调用失败,才会返回-1,从原程序调用点往下执行。
所以在redirect(char*input)重定向函数中执行用户的一个命令时必须开辟一个子进程,在字进程中调用该函数执行命令。实验中(包括第二个实验实现“打印当前时间”功能时我也直接调用exec函数,结果程序直接退出)我开始忽略这一点,没有开辟新的字进程,导致系统每次执行exec后都终止,经过查阅终于弄明白原因。这个函数的确比较特殊,之前言中的各函数都没有这种特性。
因此,一般来讲,系统调用exec和fork经常结合使用,父进程fork一个子进程,在子进程中调用exec来替换子进程的执行映像,并发的执行一些操作。
4.shell中也要注意各种命令合法性的检查。遇到错误的指令,如果按照正常方式分析执行。
就可能使出现指令为空,最常见的错误是程序出现“coredumped”提示错误然后退出。由于输入命令五花八门,很难把所有出错情况考虑清楚。因此一般来讲我尽量分析常见错误给出出错提示。
比如在多语句**现连续“;;符号的情况,程序中检测出后会直接打印出错信息并重新开始。或者语句开始就出现语句分隔符“;”另一个常见问题是如果输入的命令只有空格或“tab”键,则此时相当于无命令输入,但也要在程序中专门对此作一定处理。否则直接用exev函数调用会出现“core dumped”错误提示。
语句的一般性检查在函数int order_number(const char *input)中实现该功能,它本来用于统计以回数量,因此一般为正整数。但如果出现语句语法错误则返回负一,出特殊处理。
5.shell内部命令和外部命令的区别。最开始编写成功程序后,输入一般的命令如““ls”等命令都可以正常执行,但对于shell中非常常见的执行。如果按照此来执行会告诉你无法找到该命令。
经过查找我终于明白原因所在:系统的命令分为内部命令和外部命令两种,内部命令由的内部命令数量有限,而且绝大部分都很少用到。而每一个应用程序,我们非常熟悉的ls、cp等绝大多数命令都是外部命令,这些命令都以可执行文件的形式存在,绝大部分放在目录/bin和/sbin中。
因此,系列函数实现,但cd命令无法通过调用exec系列函数实现。作为最为常用的“自己来编写一下。这个命令也比较好编写,但新添加了我查到“pwd”命令也是shell内部命令,但却可以调用现原来目录/bin和/sbin也存在相应的可执行文件,因此可以正常执行。
应可执行文件。而cd命令在目录/bin和/sbin中没有相应可执行文件,只能通过自己编程实现。
6.cd执行权限问题。特别是对于多用户存在的操作系统权访问的文件夹。因此调用chdir函数要注意错误返回不同值给予不同的提示。
这样用户可以得知自己的错误,交互功能更强。具体实现请参见源**。7.由于本程序中涉及到输入输出重定向,因此在打印错误输出时必须使用准错误输出函数,因为当输出发生重定向后如果使用上,最后的结果也会出现问题。
这个问题之前并没有想到,出结果出现问题(当时命令为ls|wc),最后发现是因为调试中将一些提示信息也输入到管道中,最后用wc统计字符串时结果有问题。
主函数则对这种情况作cd命令,却无法通过该函数得到shell程序实现,linux外部命令都是一个单独的对于外部命令,shell最基本的更改路径功能。另外exec系列函数执行。经过检查,发linux,cd命令可能会进入自己无。
8.在使用得到当前路径函数getcwd()时,我使用如下语句:
path=getcwd(null,0); 取得当前所在路径。
这是系统会返回一个工作的绝对路径,但后来我想释放该返回的路径(语句为free(path);)却出现问题,系统告诉我内存出错,并停止执行。网上查到当该函数两个参数为空时系统会自动根据当前路径长度分配一个空间用以存储绝对路径并返回该指针。我想该函数所申请的空间是操作系统申请的,用户编程不允许自行释放。
用户只能释放编程时程序里申请的空间,因此该空间必须等到程序结束时才能全部释放。
五.实验收获和体验:1.实验中我遇到了不少问题,上面一些是我列举的比较严重也是我花费很长时间检查出的错误。主要的问题是自己对另外linux中特殊的系统调用函数也是比较新颖的一个东西,他的特性也是之前重来没有见到过的。
通过这次编程,使我对2.实验中一个难点是对于非法语句的判断。由于错误类别很多,自己编写的难处理所有的错误。很难想到所有情况,令的处理的确是一个比较头疼的问题。
3.我在要求的基础上增加一些功能,多重管道,输入输出重定向和管道混合出现,改变工作路径功能。但同实际自动补齐,和按上下键执行以前键入的命令等等。望今后有机会可以将程序进一步完善。
linux c语言中很多新的特性还不熟悉,例如管道,重定向等。linux有了更深层次的理解。
shell功能相比,自己实现的。
Linux课程习题Linux作业
作业1 linux系统基础知识 1 1.基础知识部分 必做 1.简述linux内核版本和发布版本的区别,并 举例说明 写出查看当前安装linux的发布版本信息和内核版本信息的具体命令 附上截图 答 linux的发布版本是由内核版本发展而来的。内核就是一个核心,其他软件都基于这个核心,不能直接使用。发...
linux作业
1.写出以下文件的用途。1 etc passwd 提示 通过man 5 passwd查看此文件描述说明 2 etc shadow 提示 通过man 5 shadow查看此文件描述说明 3 etc group 提示 通过man 5 group查看此文件描述说明 4 etc gshadow 提示 通过m...
LINUX作业
作业11.写出下面命令每个部分含义,字符c表示命令 command o表示选项。option oa表示选项的参数 option argument ca表示命令的参数 command argument 如 c ooa o oa ca lpr pspr n 3 linux命令行的语法结构 command...