目標
補全tsh.c
中剩余的代碼:
-
void eval(char *cmdline)
:解析并執(zhí)行命令。 -
int builtin_cmd(char **argv)
:檢測命令是否為內(nèi)置命令quit
向臀、fg
宵膨、bg
架谎、jobs
。 -
void do_bgfg(char **argv)
:實現(xiàn)bg
辟躏、fg
命令谷扣。 -
void waitfg(pid_t pid)
:等待前臺命令執(zhí)行完成。 -
void sigchld_handler(int sig)
:處理SIGCHLD
信號捎琐,即子進程停止或終止会涎。 -
void sigint_handler(int sig)
:處理SIGINT
信號,即來自鍵盤的中斷ctrl-c
野哭。 -
void sigtstp_handler(int sig)
:處理SIGTSTP
信號在塔,即終端停止信號ctrl-z
幻件。
使用make testn
用來測試你編寫的shell
執(zhí)行第n
組測試數(shù)據(jù)的輸出拨黔。
使用make rtestn
用來測試參考shell
程序第n
組測試數(shù)據(jù)的輸出(共16
組測試數(shù)據(jù))。
tshref.out
包含參考shell
程序的所有測試數(shù)據(jù)的輸出結(jié)果绰沥,先看完該文件了解命令格式在開始編碼篱蝇。
說明
可用輔助函數(shù):
-
int parseline(const char *cmdline,char **argv)
:獲取參數(shù)列表char **argv
,返回是否為后臺運行命令(true
)徽曲。 -
void clearjob(struct job_t *job)
:清除job
結(jié)構(gòu)零截。 -
void initjobs(struct job_t *jobs)
:初始化jobs
鏈表。 -
void maxjid(struct job_t *jobs)
:返回jobs
鏈表中最大的jid
號秃臣。 -
int addjob(struct job_t *jobs,pid_t pid,int state,char *cmdline)
:在jobs
鏈表中添加job
-
int deletejob(struct job_t *jobs,pid_t pid)
:在jobs
鏈表中刪除pid
的job
涧衙。 -
pid_t fgpid(struct job_t *jobs)
:返回當前前臺運行job
的pid
號。 -
struct job_t *getjobpid(struct job_t *jobs,pid_t pid)
:返回pid
號的job
奥此。 -
struct job_t *getjobjid(struct job_t *jobs,int jid)
:返回jid
號的job
弧哎。 -
int pid2jid(pid_t pid)
:將pid
號轉(zhuǎn)化為jid
。 -
void listjobs(struct job_t *jobs)
:打印jobs
稚虎。 -
void sigquit_handler(int sig)
:處理SIGQUIT
信號撤嫩。
eval
- 為避免子進程在未加入到
jobs
鏈表中就發(fā)送信號(SIGINT
、SIGCHLD
蠢终、SIGTSTP
)去處理jobs
鏈表(競爭)序攘,在子進程創(chuàng)建前需要將SIGCHLD
阻塞茴她,在加入jobs
鏈表后解鎖該阻塞。 -
fork
后的子進程會繼承父進程的信號屏蔽字并且在exec
后仍會繼承程奠,所以需要在執(zhí)行可執(zhí)行文件前復原信號屏蔽字丈牢。 -
fork
后的子進程會繼承父進程的信號處理設置,而exec
后不會繼承瞄沙。 - 參考《深入理解計算機第三版》
p543
赡麦,如何解決競爭問題。
void eval(char *cmdline)
{
char *argv[MAXARGS];
char buf[MAXLINE];
int bg;
pid_t pid;
strcpy(buf,cmdline);
bg = parseline(buf,argv);
if(argv[0] == NULL)
return;
if(!builtin_cmd(argv)){
sigset_t mask_all,mask_one,prev;
Sigfillset(&mask_all);
Sigemptyset(&mask_one);
Sigaddset(&mask_one,SIGCHLD);
Sigprocmask(SIG_BLOCK,&mask_one,&prev);
if((pid=Fork()) == 0){
setpgid(0,0);
Sigprocmask(SIG_SETMASK,&prev,NULL);
if(execve(argv[0],argv,environ) < 0){
printf("%s: Command not found.\n",argv[0]);
exit(0);
}
}
int state = bg ? BG:FG;
//parent
Sigprocmask(SIG_BLOCK,&mask_all,NULL);
addjob(jobs,pid,state,cmdline);
Sigprocmask(SIG_SETMASK,&prev,NULL);
if(!bg){
waitfg(pid);
}else{
printf("[%d] (%d) %s",pid2jid(pid),pid,cmdline);
}
}
return;
}
builtin_cmd
- 參考《深入理解計算機第三版》
p525
帕识。
int builtin_cmd(char **argv)
{
if(!strcmp(argv[0],"quit"))
exit(0);
else if(!strcmp(argv[0],"jobs")){
listjobs(jobs);
return 1;
}else if(!strcmp(argv[0],"bg") || !strcmp(argv[0],"fg")){
do_bgfg(argv);
return 1;
}
return 0; /* not a builtin command */
}
do_bgfg
void do_bgfg(char **argv)
{
if(argv[1] == NULL){
printf("%s command requires PID or %%jobid argument\n",argv[0]);
return;
}
int bg = !strcmp(argv[0],"bg");
struct job_t *job_ptr;
pid_t pid;
int jid;
if(sscanf(argv[1],"%d",&pid) > 0){
// pid
job_ptr = getjobpid(jobs,pid);
if(job_ptr == NULL || job_ptr->state == UNDEF){
printf("(%d): No such process\n",pid);
return;
}
}else if(sscanf(argv[1],"%%%d",&jid) > 0){
// jid
job_ptr = getjobjid(jobs,jid);
if(job_ptr == NULL || job_ptr->state == UNDEF){
printf("%%%d: No such job\n",jid);
return;
}
}else{
printf("%s: argument must be a PID or %%jobid\n",argv[0]);
return;
}
// get the job_ptr;
if(bg){
printf("[%d] (%d) %s",job_ptr->jid,job_ptr->pid,job_ptr->cmdline);
job_ptr->state = BG;
kill(-job_ptr->pid,SIGCONT);
}else{
// "fg"
job_ptr->state = FG;
kill(-job_ptr->pid,SIGCONT);
waitfg(job_ptr->pid);
}
return;
}
waitfg
- 使用循環(huán)檢查泛粹,簡單實現(xiàn)。
- 參考《深入理解計算機第三版》
p545
肮疗,顯示等待信號晶姊。
void waitfg(pid_t pid)
{
while(pid == fgpid(jobs))
sleep(1);
return;
}
sigchld_handler
- 參考《深入理解計算機第三版》
p539
,如何回收盡可能多的僵尸子進程伪货。
void sigchld_handler(int sig)
{
int olderrno = errno;
sigset_t mask_all,prev;
pid_t pid;
int status;
Sigfillset(&mask_all);
while((pid = waitpid(-1,&status,WNOHANG|WUNTRACED)) > 0){
if(WIFEXITED(status)){
// normally exit
Sigprocmask(SIG_BLOCK,&mask_all,&prev);
deletejob(jobs,pid);
Sigprocmask(SIG_SETMASK,&prev,NULL);
}else if(WIFSIGNALED(status)){
// exit by signal
struct job_t *job_ptr = getjobpid(jobs,pid);
Sigprocmask(SIG_BLOCK,&mask_all,&prev);
printf("Job [%d] (%d) terminated by signal %d\n",job_ptr->jid,job_ptr->pid,WTERMSIG(status));
deletejob(jobs,pid);
Sigprocmask(SIG_SETMASK,&prev,NULL);
}else{ // stop
struct job_t *job_ptr = getjobpid(jobs,pid);
Sigprocmask(SIG_BLOCK,&mask_all,&prev);
printf("Job [%d] (%d) stopped by signal %d\n",job_ptr->jid,job_ptr->pid,WSTOPSIG(status));
job_ptr->state= ST;
Sigprocmask(SIG_SETMASK,&prev,NULL);
}
}
errno = olderrno;
return;
}
sigint_handler
- 最后兩個函數(shù)们衙,均向子進程發(fā)送信號。
void sigint_handler(int sig)
{
int olderrno = errno;
pid_t pid = fgpid(jobs);
if(pid != 0){
kill(-pid,SIGINT);
}
errno = olderrno;
return;
}
sigtstp_handler
void sigtstp_handler(int sig)
{
int olderrno = errno;
pid_t pid = fgpid(jobs);
if(pid != 0){
kill(-pid,SIGTSTP);
}
errno = olderrno;
return;
}