shlab

  • 这个lab主要是考察信号的使用,还有进程创建的知识。

首先回顾一下信号处理程序的终点:

  1. 处理程序要尽可能简单。
  2. 在处理程序中只调用异步信号安全的函数。(可重入的且不能被信号处理程序中断)。
  3. 在进入处理程序时把errno 保存在某个局部变最中,在处理程序返回前恢复它。
  4. 阻塞所有的信号,保护对共享全局数据结构的访问。

首先是参数解析执行,在子进程创建过程中,设置和解除阻塞的SIGCHLD信号来避免进程在添加进job组前终止导致把不存在的子进程添加到作业列表中,书上已经给我们例子:

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
void eval(char *cmdline) 
{
char *argv[MAXARGS] = {NULL}; //初始化指针地址
int fg_bg;
pid_t pid;

sigset_t mask_all, mask_one, prev_one;

sigfillset(&mask_all); //把每个信号添加到mask_all
sigemptyset(&mask_one); //初始化mask_one为空集合
sigaddset(&mask_one, SIGCHLD); // 把SIGCHLD添加到mask_one


fg_bg = parseline(cmdline,argv) + 1; //对齐全局变量,判断是bg还是fg
if(argv[0] == NULL) return;
if(!builtin_cmd(argv))
{
sigprocmask(SIG_BLOCK, &mask_one, &prev_one); //阻塞SIGCHLD,防止子进程因SIGCHLD被回收
if((pid=fork())==0){
sigprocmask(SIG_SETMASK, &prev_one, NULL); //设置子进程解除阻塞
setpgid(0, 0); //子进程新建进程组并加入进程组
if(execve(argv[0],argv,environ)<0)
{
printf("%s:Command not found!",argv[0]);

}
exit(0);
}

sigprocmask(SIG_BLOCK, &mask_all, NULL); //父进程阻塞所有信号
addjob(jobs,pid,fg_bg,cmdline); //添加进工作组
sigprocmask(SIG_SETMASK, &prev_one, NULL); //父进程解除所有阻塞


if(fg_bg == FG) //前台命令,等待执行返回
{
waitfg(pid);
}
else //后台进程
{
int jid = pid2jid(pid);
printf("[%d] (%d) %s",jid , pid, cmdline);
}

}
return;
}

这里是判断是否为内置命令,如果是,则执行对应命令,返回1,否则返回0。

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
int builtin_cmd(char **argv) 
{
if(!strcmp(argv[0],"quit"))
{
exit(0);
}

bool m_job = !strcmp(argv[0],"jobs");
bool m_bg = !strcmp(argv[0],"bg");
bool m_fg = !strcmp(argv[0],"fg");

if(m_job)
{
listjobs(jobs);
return 1;
}
else if(m_bg || m_fg) //不能直接else,否则不是内置指令也能执行do_bgfg
{
do_bgfg(argv);
return 1;
}

return 0; /* not a builtin command */
}


执行bg和fg任务,对命令行参数进行解析,执行对应操作。

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
void do_bgfg(char **argv) 
{
int bg_fg = (!strcmp(argv[0],"bg"))+1;
struct job_t* job = NULL;
int id;
if(argv[1] == NULL)
{
printf("%s command requires PID or %%jobid argument\n",argv[0]);
return ;
}

if(argv[1][0]=='%'){
if(sscanf(&argv[1][1], "%d", &id) > 0){
job = getjobjid(jobs, id);
if(job==NULL){
printf("%%%d: No such job\n", id);
return;
}
}
}
else if(!isdigit(argv[1][0])) {
printf("%s: argument must be a PID or %%jobid\n", argv[0]);
return;
}
else{
id = atoi(argv[1]);
job = getjobpid(jobs,id);
if(job == NULL)
{
printf("%d: No such job\n", id);
return;
}
}

kill(-(job->pid),SIGCONT);

job->state = bg_fg;

if(bg_fg==BG)
printf("[%d] (%d) %s",job->jid, job->pid, job->cmdline);
else
waitfg(job->pid);
return;
}

等待前台命令执行完成。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
void waitfg(pid_t pid)
{

sigset_t mask_all, prev_all;
sigfillset(&mask_all); //把每个信号添加到mask_all
sigemptyset(&prev_all);
while(1) //反复检测进程状态
{

sigprocmask(SIG_BLOCK, &mask_all, NULL); //访问全局变量,阻塞信号
struct job_t *fg_job = getjobpid(jobs,pid);
sigprocmask(SIG_SETMASK, &prev_all, NULL);

if (!fg_job || fg_job->state != FG)
break;
sleep(1);
}


return;
}

下面是信号处理

处理终止的子进程:

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
void sigchld_handler(int sig) 
{
int olderrno = errno;
int status;
sigset_t mask_all, prev_all;
pid_t pid;
struct job_t* job;
sigfillset(&mask_all);

while((pid = waitpid(-1, &status, WNOHANG | WUNTRACED)) > 0)
{
sigprocmask(SIG_BLOCK, &mask_all, &prev_all);
job = getjobpid(jobs,pid);
if(WIFSTOPPED(status)) //子进程是因SIGSTOP停止的
{

printf("Job [%d] (%d) terminated by signal 20\n",job->jid,job->pid);
job->state = ST;
}
else if(WIFSIGNALED(status)) //子进程因SIGTSTP终止
{
printf("Job [%d] (%d) terminated by signal 2\n",job->jid,job->pid);
deletejob(jobs,pid);
}
else if (WIFEXITED(status)){ //正常终止
deletejob(jobs, pid);
}

sigprocmask(SIG_SETMASK, &prev_all, NULL);
}

errno = olderrno;


return;
}

处理ctrl+c中断,发送终止进程给前台进程。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
void sigint_handler(int sig) 
{
int olderrno = errno;
pid_t pid;
sigset_t mask_all, prev_all;
sigfillset(&mask_all);
sigprocmask(SIG_BLOCK, &mask_all, &prev_all);
if((pid = fgpid(jobs)) != 0){
sigprocmask(SIG_SETMASK, &prev_all, NULL);
kill(-pid, SIGINT);
}

errno = olderrno;
return;


}

处理ctr+z中断,挂起前台进程。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
void sigtstp_handler(int sig) 
{
int olderrno = errno;
pid_t pid;
sigset_t mask_all, prev_all;
sigfillset(&mask_all);

sigprocmask(SIG_BLOCK, &mask_all, &prev_all);

if((pid = fgpid(jobs)) > 0){
sigprocmask(SIG_SETMASK, &prev_all, NULL);
kill(-pid, SIGSTOP);
}

errno = olderrno;
return;
}