自制小shell

来源:互联网 发布:php 北京时区 编辑:程序博客网 时间:2024/05/01 16:49

曾经被面试过一个问题就是“从终端输入一个命令到显示出结果,中间的过程是怎样的”,这个问题大致要从终端、shell的定位以及linux进程树方面来回答。

有关shell的原理,可以从下面的代码看出来(摘自APUE)

#include<sys/wait.h>#include "apue.h"int main(void){    char buf[MAXLINE];    pid_t pid;    int status;    printf("%% ");    while(fgets(buf,MAXLINE,stdin)!=NULL){        if (buf[strlen(buf)-1] == '\n'){            buf[strlen(buf)-1] = 0;        }           if((pid = fork())<0){            err_sys("fork error");        }else if(pid == 0){             printf("%s",buf);            execlp(buf,buf,(char*)0);            err_ret("couldn't execute: %s",buf);            exit(127);        }           /*parent*/        if ((pid=waitpid(pid,&status,0))<0){            err_sys("waitpid error");        }           printf("%% ");    }       exit(0);}
首先fgets读取一行命令,然后fork出一个新进程来执行命令,自身等待子进程执行完毕后返回。下面说一些细节。

1. fgets当读到ctrl+D(文件结束符)时候返回NULL

2. linux的新进程产生(spawn)分为了创建和替换进程映像两部分,分别由fork和execlp完成。

fork执行之后会返回两次,一次给调用fork的主进程,返回子进程的pid,一次给子进程,返回0. 写程序的时候就可以根据这一点写各自的逻辑了。

3. execlp原型是int execlp(const char * file,const char * arg,...,(char *)0); 其中file是执行的命令,从第二个形参开始就是命令所需要的参数,从args[0]开始,所以第二个参数程序中也是buf。execlp找命令结尾是'\0'不是'\n',因此buf需要在结尾处做一下调整。execlp执行成功之后就替换了子进程的程序文件,因此后面的err_ret也不会再执行了,如果没有执行成功,就会接着执行,最后exit(127)退出。

常见错误码如下:

1: Catchall for general errors2: Misuse of shell builtins (according to Bash documentation)126: Command invoked cannot execute127: "command not found"128: Invalid argument to exit128+n: Fatal error signal "n"

如果要让本程序能接收命令参数,还需要解析buf。不过由于参数个数是不固定的,我们可以使用execlp同源的另外几个函数:

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 argv[]);int execvp(const char *file, char *const argv[]);
带l的表示参数是变长的,最后以(char*)0结尾,不带l的可以用数组的形式传参。

带p的会从系统PATH中搜素命令,不带p的则需要给出绝对路径。

0 0
原创粉丝点击