Linux下模拟实现简单的Shell
Linux下模拟实现简单的Shell
完整代码:https://github.com/JiaZhengJingXianSheng/Linux_Shell
一、 fork
fork系统调用用于创建一个新进程,称为子进程,它与进程(称为系统调用fork的进程)同时运行,此进程称为父进程。创建新的子进程后,两个进程将执行fork()系统调用之后的下一条指令。子进程使用相同的程序计数器,相同的CPU寄存器,在父进程中使用的相同打开文件。
特点
1)在父进程中,fork返回新创建子进程的进程ID; 2)在子进程中,fork返回0; 3)如果出现错误,fork返回一个负值;
如果创建新进程成功,则出现两个进程,一个是子进程,一个是父进程。在子进程中,fork函数返回0,在父进程中,fork返回新创建子进程的进程ID。因此我们可以通过判断fork返回值来确定进程是子进程还是父进程。
二、 execvp
execvp() 会从PATH所指的目录中查找符合参数file 的文件名,找到后便执行该文件,然后将第二个参数 argv 传给该欲执行的文件。
如果执行成功则函数不会返回,执行失败则直接返回-1,失败原因存于errno中。我们可以通过 perror() 函数来打印错误。
三、 wait/waitpid
wait: 父进程一旦调用了wait就立即阻塞自己,由wait自动分析是否当前进程的某个子进程已经退出,如果让它找到了这样一个已经变成僵尸的子进程,wait就会收集这个子进程的信息,并把它彻底销毁后返回;如果没有找到这样一个子进程,wait就会一直阻塞在这里,直到有一个出现为止。
waitpid: 暂时停止目前进程的执行, 直到有信号来到或子进程结束. 如果在调用时子进程已经结束, 则会立即返回子进程结束状态值. 子进程的结束状态值会由参数status 返回, 而子进程的进程识别码也会一快返回.
四、 实现
1. 相关函数
1 | pid_t pid = fork(); |
调用fork函数可创建一个子进程,这边说的子进程就是我们平时所讲的线程。只不过在Linux下线程是用拷贝进程的形式实现,所以这边称为子进程。
1 | int execvp(const char *__file, char *const *__argv) |
execvp函数可以调用PATH下的命令,其中参数1为操作码(命令),参数2为操作码+操作数(命令+参数)。
1 | perror("fork error"); |
eperror函数用来打印错误
1 | wait(NULL); |
在父进程中调用wait函数,会阻塞父进程,并检查子进程是否退出,未退出则杀死子进程。
1 | chdir(); |
由于父进程和子进程并不共享环境变量,子进程修改了当前工作目录的环境变量对父进程也没有什么影响,所以cd命令是无法简单用execvp函数实现的,这边会用到chdir函数。
2. 具体实现
首先从键盘读取一行命令。并将命令根据空格划分开,第一个参数即为命令,后续为参数。
1 | char cmd[64]; |
1 | char *command[64]; |
至此我么就将命令按格式全部划分。
接下来我们去创建一个子线程,在子线程内,判断命令如果为cd则调用chdir(), 否则调用execvp() , 并用perror(), 打印错误。在父进程调用wait() 函数来回收子进程。
1 | if (strcmp(command[0], "exit") == 0) |
3. 结果
1 | lyz@ubuntu:~/Desktop/LinuxHomework2$ g++ MyShell.cpp -o SHELL |
我们的shell运行正常