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 {              /* The job struct */
pid_t pid; /* job PID */
int jid; /* job ID [1, 2, ...] */
int state; /* UNDEF, BG, FG, or ST */
char cmdline[MAXLINE]; /* command line */
};

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; /* not a builtin command */
}

3.3 do_bgfg

在后台执行的格式为

1
>tsh fg/bg %[num]

第一步先判断是否为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;
}