[施工完成] CSAPP shell lab

背景

动手实现一个简单的Lab,主要依赖于课本第八章的内容 感觉主要是05比较难。。发现执行的顺序不太对。。原因是SIGCHLD里面waitpid参数没写对。。 后面的就相对简单了 累计大概花了10个小时的样子

实现细节

built-in comamnd

built-in command 指的是shell自身的命令,因此只有少数几个,比如pwd.在上使用which pwd的时候,会提示"pwd: shell built-in command"

测试文件的构成

以trace04.txt举例

1#
2# trace04.txt - Run a background job.
3#
4/bin/echo -e tsh> ./myspin 1 \046
5./myspin 1 &

这是两条测试命令。。第一条是调用了 "/bin/echo -e"来执行,而且这条命令是一个fg job, \046 是'&'的ascii,这是输出字符串的一部分。

子进程中的log打印不正确

有些执行路径的log没有打印出来 可以调用flush(stdout) 确保打印

在执行fg job的时候,会等待上一个未结束的bg job执行

这是因为waitpid中的option写了默认的0,没有传入正确的option导致的

SIGCHLD的实现

感觉这个函数是整个Lab的难点

注意这里除了要对正常结束的process处理以外,也要处理因为其他原因导致的进程退出 可以根据下图的内容来判断是因为哪种原因进程结束的 这段调了好久。。虽然书上已经讲了比较多的情况。。。不过还是觉得略难

 1void sigchld_handler(int sig) {
 2	int olderrno = errno;
 3	sigset_t mask_all, prev_all;
 4	pid_t pid;
 5	int status;
 6	Sigfillset(&mask_all);
 7
 8	while ((pid = waitpid(-1, &status, WNOHANG | WUNTRACED)) > 0) {
 9		if (WIFEXITED(status)) {
10			Sigprocmask(SIG_BLOCK, &mask_all, &prev_all);
11			deletejob(jobs, pid);
12			Sigprocmask(SIG_SETMASK, &prev_all, NULL);
13		} else if (WIFSIGNALED(status)) {
14			printf("Job (%d) [%d] terminated by signal %d\n",
15			       pid2jid(pid), pid, WTERMSIG(status));
16			Sigprocmask(SIG_BLOCK, &mask_all, &prev_all);
17
18			deletejob(jobs, pid);
19			Sigprocmask(SIG_SETMASK, &prev_all, NULL);
20		} else if (WIFSTOPPED(status)) {
21			printf("Job (%d) [%d] stopped by signal %d\n",
22			       pid2jid(pid), pid, WSTOPSIG(status));
23			struct job_t* job = getjobpid(jobs, pid);
24			if (job != NULL) {
25				job->state = ST;
26			}
27		}
28	}
29	errno = olderrno;
30}
31

do_bgfg

一开始可能会有些纠结。。怎么转换bg和fg呢。。 实际上直接改变state就好了 所谓bg job还是 fg job,其实区别就在于当前process要不要等其结束。。并没有什么本质区别

 1void do_bgfg(char** argv) {
 2	char* cmd = argv[0];
 3	char* id = argv[1];
 4	if (id == NULL) {
 5		printf("%s command requires PID or %%jobid argument\n", cmd);
 6		return;
 7	}
 8
 9	struct job_t* job;
10	if (id[0] == '%') {
11		int jid = atoi(&id[1]);
12		if (jid == 0) {
13			printf("%s: argument must be a PID or %%jobid\n", cmd);
14			return;
15		}
16		job = getjobjid(jobs, jid);
17		if (job == NULL) {
18			printf("%%%d: No such job\n", jid);
19			return;
20		}
21
22	} else {
23		pid_t pid = atoi(id);
24		if (pid == 0) {
25			printf("%s: argument must be a PID or %%jobid\n", cmd);
26			return;
27		}
28		job = getjobpid(jobs, pid);
29		if (job == NULL) {
30			printf("(%d): No such process\n", pid);
31			return;
32		}
33	}
34
35	Kill(-job->pid, SIGCONT);
36	if (cmd[0] == 'b') {
37		job->state = BG;
38		printf("[%d] (%d) %s", pid2jid(job->pid), job->pid,
39		       job->cmdline);
40
41	} else {
42		job->state = FG;
43		// 变为fg后,需要一直等到结束
44		waitfg(job->pid);
45	}
46}

eval

直接让写可能确实比较有难度。。好在课本以及lab的说明上给了足够多的示例代码和hint.. 所以难度其实还好

 1
 2void eval(char* cmdline) {
 3	char* parsed_args[MAXARGS];
 4	char buf[MAXLINE];
 5
 6	strcpy(buf, cmdline);
 7	int bg = parseline(cmdline, &parsed_args[0]);
 8	if (parsed_args[0] == NULL) {
 9		// ignore empty lines
10		return;
11	}
12	sigset_t mask_all, mask_one, prev_one;
13	// mask_all 是屏蔽全部信号
14	// mask_one 是屏蔽SIGCHLD信号
15	Sigfillset(&mask_all);
16	Sigemptyset(&mask_one);
17	Sigaddset(&mask_one, SIGCHLD);
18
19	if (builtin_cmd(parsed_args) == 0) {
20		pid_t pid;
21		// printf("bg:%d cmdline :%s\n", bg, cmdline);
22
23		// block SIGCHLD
24		Sigprocmask(SIG_BLOCK, &mask_one, &prev_one);
25		if ((pid = Fork()) == 0) {
26			// child process
27			// printf("in child process\n");
28			Sigprocmask(SIG_SETMASK, &prev_one,
29				    NULL);  // unblock SIGCHLD
30			// printf("before setpgid\n");
31			setpgid(0, 0);
32			// printf("before execve\n");
33
34			// 打log记得调用fflush,不然可能还没来得及输出到屏幕上就exit了
35			fflush(stdout);
36			fflush(stdout);
37			// 现在的background是虚假的。。其实还是会等待。。
38			// 如果后面都是bg指令,就不会等待,但是如果有fg指令,就会等待。
39			Execve(parsed_args[0], parsed_args, environ);
40		}
41		//	printf("pid =%d\n", pid);
42		// printf("before add job block all signals\n");
43		Sigprocmask(SIG_BLOCK, &mask_all, NULL);
44		addjob(jobs, pid, bg ? BG : FG, cmdline);
45		Sigprocmask(SIG_SETMASK, &prev_one, NULL);
46
47		// parent wait child
48		if (!bg) {
49			waitfg(pid);
50		} else {
51			printf("[%d] (%d) %s", pid2jid(pid), pid, cmdline);
52		}
53	}
54	return;
55}
56

完整代码参考: 这里

Posts in this Series