CSAPP shell lab
1. 给出的函数功能
eval(char cmdline)
:处理输入的一行命令
parse
: 对输入的一行进行解析
builtin_cmd
: 判断是否为内置的命令,比如quit
do_bgfg
:在后台执行程序
waitfg
:当进程不是前台程序的时候一直被阻塞
sigchld_handler
:当子进程终止的时候向父进程发送相应信号
sigint_handler
:终止的时候发送信号给父进程
sigtstp_handler
:处理停止信号(ctrl + z)
clearjob
:将存储job的结构体清空为0
一个job的结构体如下
1 2 3 4 5 6
| struct job_t { pid_t pid; int jid; int state; char cmdline[MAXLINE]; };
|
initjobs
:初始化job列表
maxjid
: 返回最大的jobid
addjob
:向job列表中加入job
int deletejob(struct job_t *jobs, pid_t pid)
:从job列表中删除pid 对应的job
pid_t fgpid(struct job_t *jobs)
:返回前台工作
struct job_t *getjobpid(struct job_t *jobs, pid_t pid)
返回特定的job
struct job_t *getjobjid(struct job_t *jobs, int jid)
:通过特定的jobid返回job
pid2jid
: 通过pid得到jid
listjobs
:打印出job
2. 所需系统函数
2.1 kill
int kill(pid_t pid, int sig):
当第一个参数为0的时候,将sig传送给相应的pid
当参数为-1的时候,将sig传送给所有的进程
当参数为负数的时候,返回abs(pid)对应的gid
2.2 execve
int execve(const char *pathname, char * const argv[], char * const envp[])
pathname 是程序的位置
argv 是程序的参数, 比如设置的-l -g之类
envp 是环境变量
3. code
3.1 eval
思路:读入一行数据,判断是否存在,不存在直接然会,如果是系统程序,那么创建子进程执行程序, 然后判断是否为background,不是后台程序那么要等待该程序结束
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| void eval(char *cmdline) { char* argv[MAXARGS]; pid_t pid; int bg; bg = parseline(cmdline, argv); if (argv[0] == NULL) return; if (!builtin_cmd(argv)) { if ((pid=fork())==0) { if (execve(argv[0], argv, environ) < 0) unix_error("execv error"); } addjob(jobs, pid, (bg==1? BG:FG), cmdline); if (!bg) waitfg(pid); else printf("[%d] (%d) %s", pid2jid(pid), pid, cmdline); } return; }
|
3.2 builtin_cmd
内置的命令有jobs和quit
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| int builtin_cmd(char **argv) { if (!strcmp(argv[0], "quit")) { exit(0); } if (!strcmp(argv[0], "jobs")) { listjobs(jobs); return 0; }
if (!strcmp(argv[0], "bg") || !strcmp(argv[0], "fg")) { do_bgfg(argv[0]); return 1; }
if (!strcmp(argv[0], "&")) return 1; return 0; }
|
3.3 do_bgfg
在后台执行的格式为
第一步先判断是否为fg/bg, 合法后加入到jobs中去
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| void do_bgfg(char **argv) { if (argv[0] == NULL) { printf("%s command requires PID or %%jobid argurement\n",argv[0]); return; } int id; struct job_t* job; if (sscanf(argv[1], "%%%d", &id)>0) { if (!(job = getjobpid(jobs, id))) { printf("(%d): No such process\n", id); return; } } else if (sscanf(argv[1], "%d", &id) > 0){ if(!(job = getjobpid(jobs, id))) { printf("(%d)No such process", id); return; } } else { printf("The input must the the pid\n"); } job->state = argv[0][0] = 'b' ? BG : FG; kill(-job->pid, SIGCONT); return; }
|
3.4 waitfg
对进程进行阻塞直到进程的pid不再等于在前台运行的进程ID
1 2 3 4 5 6 7 8
| void waitfg(pid_t pid) { struct job_t *job = getjobpid(jobs, pid); while (job->pid == FG) { sleep(1); } return; }
|
3.5 sigchld_handler
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| void sigchld_handler(int sig) { pid_t pid; int status; while ((pid = waitpid(-1, &status, WNOHANG | WUNTRACED)) > 0) { if (WIFCONTINUED(status)){ printf("Job [%d] (%d) stopped by the signal %d\n", pid2jid(pid), pid, WSTOPSIG(status)); getjobpid(jobs, pid)->state = ST; } else { if (WIFSIGNALED(status)) { printf("Job [%d] (%d) terminated by signal %d\n", pid2jid(pid), pid, WTERMSIG(status)); } deletejob(jobs, pid); } } return; }
|
3.6 sigint_handler
主要流程就是获得前台进程的id,然后对相应的
1 2 3 4 5 6 7 8 9
| void sigint_handler(int sig) { pid_t fpid = fgpid(jobs); if (fpid) { kill(-fpid, sig); } return; }
|