linux中shell编程怎样运行,【Unix/Linux编程实践】shell如何运行程序—编写命令解析器sh...

1.shell是如何运行程序的?

shell由下面的循环组成:

while (!end_of_input)

getcommand

execute command

wait for command to finish

我们可以实际用一下shell:

jiange@jiange-Inspiron:~/cpp$ls

override override.cpp test test1.cpp test.cpp test.o test.sh

jiange@jiange-Inspiron:~/cpp$ps

PID TTY TIME CMD

2457 pts/12 00:00:00 bash

3780 pts/12 00:00:00 ps

上述过程为:

sh读入ls,新建一个进程ls并执行,此时sh则等待ls的退出,ls退出后回到sh,sh继续读入ps……

因此,要编写sh,我们需要知道如何运行一个程序,创建,终止一个进程,以及一个进程如何等待另一个进程的结束。

2.如何执行一个程序

答案:

execvp(progname, arglist)

它的运作流程:

程序调用execvp;

内核从磁盘将程序载入;

内核将arglist复制到进程;

内核调用main(argc, argv)。

运行ls的例子:

/* exec1.c - shows how easy it is for a program to run a program

*/

main()

{

char *arglist[3];

arglist[0] = "ls";

arglist[1] = "-l";

arglist[2] = 0 ;

printf("* * * About to exec ls -l\n");

execvp( "ls" , arglist );

printf("* * * ls is done. bye\n");

}

执行结果:

$ ./exec1

* * * About to exec ls -l

总用量 40

-rwxrwxr-x 1 jiange jiange 7370 12月 7 22:59 exec1

-rw-rw-r-- 1 jiange jiange 262 10月 14 2007 exec1.c

-rw-rw-r-- 1 jiange jiange 406 10月 14 2007 forkdemo1.c

-rw-rw-r-- 1 jiange jiange 315 10月 14 2007 forkdemo2.c

-rw-rw-r-- 1 jiange jiange 503 10月 14 2007 forkdemo3.c

-rw-rw-r-- 1 jiange jiange 1395 10月 14 2007 psh1.c

-rw-rw-r-- 1 jiange jiange 1766 10月 14 2007 psh2.c

-rw-rw-r-- 1 jiange jiange 784 10月 14 2007 waitdemo1.c

-rw-rw-r-- 1 jiange jiange 1077 10月 14 2007 waitdemo2.c

一切好像正常运行,不过,为什么printf(“* * * ls is done. bye\n”);这一句没有执行呢?!

这就要理解下execvp的作用了:execvp将程序从磁盘载入当前进程中,替代当前进程的代码和数据,所以执行完execvp之后,进程exec1完全被替换掉了,自然不会执行后续的打印工作。

2.带提示符的shell

这个版本提示用户输入程序名和参数,然后运行。

/* prompting shell version 1 * Prompts for the command and its arguments. * Builds the argument vector for the call to execvp. * Uses execvp(), and never returns. */

#include

#include

#include

#define MAXARGS 20 /* cmdline args */

#define ARGLEN 100 /* token length */

int main()

{

char *arglist[MAXARGS+1]; /* an array of ptrs */

int numargs; /* index into array */

char argbuf[ARGLEN]; /* read stuff here */

char *makestring(); /* malloc etc */

numargs = 0;

while ( numargs < MAXARGS )

{

printf("Arg[%d]? ", numargs);

if ( fgets(argbuf, ARGLEN, stdin) && *argbuf != '\n' )

arglist[numargs++] = makestring(argbuf);

else

{

if ( numargs > 0 ){ /* any args? */

arglist[numargs]=NULL; /* close list */

execute( arglist ); /* do it */

numargs = 0; /* and reset */

}

}

}

return 0;

}

int execute( char *arglist[] )

/* * use execvp to do it */

{

execvp(arglist[0], arglist); /* do it */

perror("execvp failed");

exit(1);

}

char * makestring( char *buf )

/* * trim off newline and create storage for the string */

{

char *cp, *malloc();

buf[strlen(buf)-1] = '\0'; /* trim newline */

cp = malloc( strlen(buf)+1 ); /* get memory */

if ( cp == NULL ){ /* or die */

fprintf(stderr,"no memory\n");

exit(1);

}

strcpy(cp, buf); /* copy chars */

return cp; /* return ptr */

}

上面使用makestring函数来动态分配空间给字符串参数。

运行上面程序,我们就可以传递参数进去了。

不过一个比较严重的缺陷:运行完ls之后,整个程序就退出了,而我们期待的是,运行完ls后返回到我们的shell中,shell可以继续接收命令输入并执行。

在开头我们讲到,sh读入ls,新建一个进程ls并执行,此时sh则等待ls的退出,ls退出后回到sh。

也就是说,sh并不直接执行ls命令,而是新建了一个进程来执行它,那么问题来了:如何建立新的进程?

答案:使用fork();

我们可以使用fork来创建子进程,然后新的进程调用execvp来执行用户命令。

那么,父进程sh如何等待子进程ls的结束呢?

答案:使用 pid=wait(&status);

wait阻塞调用它的进程直到子进程结束,之后wait取得子进程结束时传给exit的值(通过status),并返回子进程的PID。

3.可以持续接收用户命令的shell

经过以上分析,我们可以设计新的shell:

获取命令-> 用fork创建新进程 ->父进程wait ->子进程exec运行新程序->在新程序的main中运行->新程序exit -> 父进程得到子进程状态->获取命令

/** prompting shell version 2 ** ** Solves the `one-shot' problem of version 1 ** Uses execvp(), but fork()s first so that the ** shell waits around to perform another command ** New problem: shell catches signals. Run vi, press ^c. **/

#include

#include

#define MAXARGS 20 /* cmdline args */

#define ARGLEN 100 /* token length */

main()

{

char *arglist[MAXARGS+1]; /* an array of ptrs */

int numargs; /* index into array */

char argbuf[ARGLEN]; /* read stuff here */

char *makestring(); /* malloc etc */

numargs = 0;

while ( numargs < MAXARGS )

{

printf("Arg[%d]? ", numargs);

if ( fgets(argbuf, ARGLEN, stdin) && *argbuf != '\n' )

arglist[numargs++] = makestring(argbuf);

else

{

if ( numargs > 0 ){ /* any args? */

arglist[numargs]=NULL; /* close list */

execute( arglist ); /* do it */

numargs = 0; /* and reset */

}

}

}

return 0;

}

execute( char *arglist[] )

/* * use fork and execvp and wait to do it */

{

int pid,exitstatus; /* of child */

pid = fork(); /* make new process */

switch( pid ){

case -1:

perror("fork failed");

exit(1);

case 0:

execvp(arglist[0], arglist); /* do it */

perror("execvp failed");

exit(1);

default:

while( wait(&exitstatus) != pid )

;

printf("child exited with status %d,%d\n",

exitstatus>>8, exitstatus&0377);

}

}

char *makestring( char *buf )

/* * trim off newline and create storage for the string */

{

char *cp, *malloc();

buf[strlen(buf)-1] = '\0'; /* trim newline */

cp = malloc( strlen(buf)+1 ); /* get memory */

if ( cp == NULL ){ /* or die */

fprintf(stderr,"no memory\n");

exit(1);

}

strcpy(cp, buf); /* copy chars */

return cp; /* return ptr */

}

关于格式控制方面,比如如何在一行中输入参数,如何用exit退出shell等等,在这里我们不做讨论。

在运行的时候,我们发现一个严重的错误:

假如我们正在运行一个子进程,这时我们键入Ctrl-C键,我们预期的应该是子进程终止,而shell仍继续运行,但事实是shell也终止了,因为键盘信号SIGINT发给了所有连接的进程!

为使得有子进程在运行时,shell不会因为中断信号而跟着终止,我觉得可以这样:只要有子进程运行,shell就将对SIGINT信号的响应设为ignore,子进程结束之后,则设置为默认方式。这样子shell就能在子进程运行的时候屏蔽中断信号。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值