简介
编写一个支持工作控制的Unix Shell程序,使自己更熟悉进程控制和信号
需要实现的功能点
- 能运行几个内置的命令,如
fg
bg
- 能运行指定的程序,如
/bin/ls
- 能区分前后台程序,包括切换前后台程序
- 能接受从程序内部和外部分别发送的信号,如
SIGINT
SIGTSTOP
- 对非法命令的错误处理和提示
注意点
- 使用测试文件来推动代码的完善,正确结果可以参考
tshref
- 在kill函数中发送SIGINT和SIGTSTP信号的时候,记得使用
-pid
,表示给整个进程组发送信号。 - 关于在eval里面要使用sigprocmask函数在addjob之前阻塞住SIGCHLD,不然在子进程执行完的时候如果还没有执行addjob,就会给父进程shell发送SIGCHLD,这时候调用sigchld_handler在addjob之前deletejob一个根本不存在的job,容易出现竞争条件。同时,阻塞之后要及时释放锁,释放锁的地方有三个地方,
一是:在子进程执行execve,即子进程执行其他程序的时候需要把锁还原。
二是:在父进程addjob之后要及时归还锁。
三是:不能把sigprocmask阻塞进程在build_cmd判断外面,不然会出现锁住了没有归还锁的情况。 - 当按下ctrl-c或者ctrl-z的时候,内核会发送SIGINT或者SIGTSTP信号给所有前台进程组,因为shell是前台进程,那么从shell中fork出的子进程都属于shell这个前台进程组的,那么我们每次SIGINT或者SIGTSTP信号都会给我们的所有进程,这明显是不合理的。所以当我们在每次fork出子进程的时候,都要利用setpgid(0,0)把新加的进程添加到新的进程组中。
- 为了实现前后台功能,shell引入了job这个概念,表示为了一条命令行求值而创建的进程。
- test16要求把子进程自己给自己发送SIGINT信号和SIGSTSP信号,我们之前只讨论过通过ctrl-z和ctrl-c向shell前台发送。对于子进程自己给自己发送SIGINT信号和SIGSTSP信号的唯一改变就是,shell外壳不会主动调用sigint_handler和sigstsp_handler,只有在子进程终止或者停止的时候,会发送信号给父进程,此时父进程通过调用sigchld_handler回收僵尸进程。那么,在sigchld_handler根据得到信号调用来判断不同的情况,并选择调用sigint_handler和sigstsp_handler。
代码讲解
大部分函数都已经完成,需要特别完善的函数有eval(char *cmdline)
do_bgfg(char **argv)
builtin_cmd(char **argv)
以及三个信号处理程序void sigchld_handler(int sig)
void sigtstp_handler(int sig)
void sigint_handler(int sig)
eval
1 | void eval(char *cmdline) |
该函数首先判断命令是否是内嵌命令;如果不是,则产生一个新的进程来执行命令,并将这个命令对应的job加入工作列表;如果是前台进程,则要显式等待该程序结束
sigchld_handler
1 | void sigchld_handler(int sig) |