这个实验需要我们在tsh.c中补充相关代码实现一个shell程序(废话ing)
那么根据README文档中的顺序进行补齐
- 添加相关的宏定义和头文件
1 2 3 4 5
| #define _POSIX_C_SOURCE 199309L #define _XOPEN_SOURCE 500
#include <sys/select.h> #include <sys/time.h>
|
eval
解析和解释命令行的主例程
1. 实现内容
- 若用户请求的是内置命令(quit、jobs、bg 或 fg)则立即执行。否则,创建一个子进程并在该子进程的上下文中运行该任务
- 若任务在前台运行,等待其终止后再返回
- 每个子进程必须具有唯一的进程组ID,这样在键盘上输入ctrl-c(ctrl-z)时,后台运行的子进程就不会从内核接收SIGINT(SIGTSTP)信号
2. 代码实现
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 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58
| void eval(char *cmdline) { char* argv[MAXARGS]; int state = UNDEF; sigset_t set; pid_t pid;
if(parseline(cmdline, argv) == 1) { state = BG; }else{ state = FG; } if(argv[0] == NULL) return;
if(!builtin_cmd(argv)){ if(sigemptyset(&set) < 0){ unix_error("sigemptyset error"); } if(sigaddset(&set, SIGINT) < 0 || sigaddset(&set, SIGTSTP) < 0 || sigaddset(&set, SIGCHLD) < 0){ unix_error("sigaddset error"); } if(sigprocmask(SIG_BLOCK, &set, NULL) < 0){ unix_error("sigprocmask error"); } if((pid = fork()) < 0){ unix_error("fork error"); }else if(pid == 0){ if(sigprocmask(SIG_UNBLOCK, &set, NULL) < 0){ unix_error("sigprocmask error"); } if(setpgid(0, 0) < 0){ unix_error("setpgid error"); } if(execve(argv[0], argv, environ) < 0){ printf("%s: Command not found\n", argv[0]); exit(0); } }
addjob(jobs, pid, state, cmdline); if(sigprocmask(SIG_UNBLOCK, &set, NULL) < 0){ unix_error("sigprocmask error"); } if(state == FG){ waitfg(pid); }else{ printf("[%d] (%d) %s", pid2jid(pid), pid, cmdline); } } return; }
|
builtin_cmd
识别和解释内置命令:quit、fg、bg和jobs
1. 实现内容
- 如果用户输入的是内置命令,则立即执行该命令
2. 代码实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| int builtin_cmd(char **argv) { if(!strcmp(argv[0], "quit")){ exit(0); }else if(!strcmp(argv[0], "bg") || !strcmp(argv[0], "fg")){ do_bgfg(argv); }else if(!strcmp(argv[0], "jobs")){ listjobs(jobs); }else{ return 0; } return 1; }
|
do_bgfg
1. 实现内容
执行内置的bg和fg命令
2. 代码实现
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 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53
| void do_bgfg(char **argv) { if(!argv[1]){ printf("%s command requires PID or %%jobid argument\n", argv[0]); return; }
int jid = -1; pid_t pid = -1;
if(argv[1][0] == '%'){ jid = atoi(&argv[1][1]); if(jid <= 0) { printf("%s: argument must be a PID or %%jobid\n", argv[0]); return; } }else{ pid = atoi(argv[1]); if (pid <= 0) { printf("%s: argument must be a PID or %%jobid\n", argv[0]); return; } }
struct job_t *job = NULL; if(jid > 0){ job = getjobjid(jobs, jid); if(!job){ printf("%%%d: No such job\n", jid); return; } }else{ job = getjobpid(jobs, pid); if (!job) { printf("(%d): No such process\n", pid); return; } } kill(-(job->pid), SIGCONT); if(strcmp(argv[0], "fg") == 0){ job->state = FG; waitfg(job->pid); }else{ job->state = BG; printf("[%d] (%d) %s", job->jid, job->pid, job->cmdline); } }
|
waitfg
1. 实现内容
waitfg - 等待进程pid不再处于前台运行状态(该命令会阻塞当前进程,直到指定pid的进程退出前台运行状态)
2. 代码实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| void waitfg(pid_t pid) { if(pid == 0)return;
while (fgpid(jobs) == pid){ sleep(0.1); }
usleep(10000);
}
|
sigchld_handler
1. 实现内容
每当一个子作业终止(成为僵尸进程),或因收到SIGSTOP或SIGTSTP信号而停止时,内核会向shell发送SIGCHLD信号。该处理器会回收所有可用的僵尸子进程,但不会等待任何其他当前正在运行的子进程终止
2. 代码实现
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 28 29 30 31 32 33 34
| void sigchld_handler(int sig) { int old_errno = errno; pid_t pid; int status;
while((pid = waitpid(-1, &status, WNOHANG | WUNTRACED)) > 0){ if(WIFEXITED(status)){ deletejob(jobs, pid); }else if(WIFSIGNALED(status)){ printf("Job [%d] (%d) terminated by signal %d\n", pid2jid(pid), pid, WTERMSIG(status)); deletejob(jobs, pid); }else if(WIFSTOPPED(status)){
printf("Job [%d] (%d) stopped by signal %d\n", pid2jid(pid), pid, WSTOPSIG(status)); struct job_t *job = getjobpid(jobs, pid); if(job){ job->state = ST; } } } errno = old_errno;
}
|
sigint_handler
1. 实现内容
当用户在键盘上输入ctrl-c时,内核会向shell发送SIGINT信号。捕获该信号并将其传递给前台作业。
2. 代码实现
1 2 3 4 5 6 7 8 9
| void sigint_handler(int sig) { pid_t fg_pid = fgpid(jobs); if(fg_pid != 0){ kill(-fg_pid, SIGINT); } }
|
sigtstp_handler
1. 实现内容
当用户在键盘上输入ctrl-z时,内核会向shell发送SIGTSTP信号。捕获该信号并通过向当前前台作业发送SIGTSTP信号来暂停该作业
2. 实现代码
1 2 3 4 5 6 7 8 9
| void sigtstp_handler(int sig) { pid_t fg_pid = fgpid(jobs); if(fg_pid != 0){ kill(-fg_pid, SIGTSTP); } }
|
检测
1 2
| make test01 make rtest01
|
- 对比两次输出,基本一样即为通过
- 一共要测试16个test