vimer linux kernel 爱好者

unix之shell解释器(exec)

2017-05-12

在unix中,一个shell的执行流程如下:

读入命令

时间轴
------------------------>>

      ___                    ____
sh----|  |-------------------|   |
 ^    |__|                   |___|
 |      |    新进程            |
 |      |____>"ls"--->退出     |____>"ps"
"ls"

一个程序如何运行另一个程序?

使用execvp,这个函数有两个参数:要运行的程序名称和这个程序的命令行参数组。当程序运行时命令行参数以argv[]的形式传递给程序. 注意,将数组的第一个元素设置为程序的名称,最后一个元素必须是Null.

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int main()
{
	char *arglist[3];

	arglist[0] = "ls";
	arglist[1] = "-l";
	arglist[2] = 0;
	printf("***中断这个程序的执行***\n");
	execvp("ls", arglist);
	printf("ls is done, bye\n");
}
yubo@debian:~/test/tmp/unix/shell$ ./exec
***中断这个程序的执行***
总用量 88
-rwxr-xr-x 1 yubo yubo  6108  5月 18 06:40 exec
-rw-r--r-- 1 yubo yubo   407  5月 18 07:08 exec1.c
-rw-r--r-- 1 yubo yubo   801  5月 16 19:28 execute.c
-rw-r--r-- 1 yubo yubo 29520  5月 16 19:05 segfault
-rwxr-xr-x 1 yubo yubo 13680  5月 16 19:30 smsh
-rw-r--r-- 1 yubo yubo   811  5月 16 19:30 smsh1.c
-rw-r--r-- 1 yubo yubo   389  5月 16 19:09 smsh.h
-rw-r--r-- 1 yubo yubo  2630  5月 16 19:30 splitline.c
-rwxr-xr-x 1 yubo yubo  6188  5月 16 19:49 test
-rw-r--r-- 1 yubo yubo   265  5月 16 19:48 test.c

这里你有没有注意到上面程序中的第二条printf语句消息了。这里书上的解释是:内核将新进程载入到当前进程,替换了当前进程的代码和数据,也就是说,execvp这个函数将“ls”这个命令加入到当前的程序,代替了后面的语句,从而使后面的printf语句消失掉。

下面的代码更深入一步:

#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <string.h>
#include <unistd.h>

#define MAXARGS		20 /* cmdline args */
#define ARGLEN		100 /* token length */

int execute(char *const arglist[])
{
	execvp(arglist[0], arglist);
	perror("execvp failed");
	exit(1);
}

char *makestring(char *buf)
{
	char *cp;
	buf[strlen(buf) - 1] = '\0';
	cp = malloc(strlen(buf) + 1);
	if (cp == NULL) {
		fprintf(stderr, "no memory\n");
		exit(1);
	}

	strcpy(cp, buf);
	return cp;

}

int main()
{
	char *arglist[MAXARGS + 1];
	int numargs;
	char argbuf[ARGLEN]; /* read stuff from here */
	numargs = 0;

	while( numargs < MAXARGS) {
		printf("Arg[%d]?", numargs);
		if (fgets(argbuf, ARGLEN, stdin) && *argbuf != '\n') {
			arglist[numargs++] = makestring(argbuf);
		}
		else {
			if (numargs > 0) {
			printf("%s", argbuf);
				arglist[numargs] = NULL;
				if (execute(arglist))
				numargs = 0;
			}
		}
	}
	return 0;
}


下面是演示:

yubo@debian:~/test/tmp/unix/shell$ ./psh
Arg[0]?ls
Arg[1]?-l
Arg[2]?.
Arg[3]?

总用量 28
-rwxr-xr-x 1 yubo yubo 6108  5月 18 18:01 exec
-rw-r--r-- 1 yubo yubo  413  5月 18 18:01 exec1.c
-rwxr-xr-x 1 yubo yubo 9012  5月 18 19:17 psh
-rw-r--r-- 1 yubo yubo 1116  5月 18 19:17 psh1.c
yubo@debian:~/test/tmp/unix/shell$ ls
exec  exec1.c  psh  psh1.c

这个代码,条理还是比较清楚的。

如何输入连续的shell命令

上面的程序只可运行一次,如果想继续使用,需要再一次运行程序。那么,如何使用真正的shell呢?

答案是fork().

这篇文章里,我们详细讲解了fork的用法。

#include <stdio.h>
#include <signal.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>

#define MAXARGS		20
#define ARGLEN		100

void execute(char **); /* execvp */
char *makestring(char *); /* to restore args vector */

int main()
{
	char *arglist[MAXARGS + 1];
	int numargs = 0;
	char argbuf[ARGLEN];

	while(numargs < MAXARGS){
		printf("Arg[%d]", numargs);
		if (fgets(argbuf, ARGLEN, stdin) && *argbuf != '\n')
			arglist[numargs++] = makestring(argbuf);
		else {
			if (numargs > 0) {
				arglist[numargs] = NULL;
				execute(arglist);
				numargs = 0;
			}

		}
	}
	return 0;
}

void execute(char *arglist[])
{
	int pid, existstatus;

	pid = fork();	/* make new process */
	switch(pid){
		case -1:
			perror("fork failed");
			exit(1);
		case 0:
			execvp(arglist[0], arglist);
			perror("execvp failed");
			exit(1);
		default:
			while(wait(&existstatus) != pid)
				;
			printf("child exited with status %d, %d\n",
					existstatus >> 8, existstatus &0377);
	}
}
char *makestring(char *buf)
{
	char *cp;
	buf[strlen(buf) - 1] = '\0';
	cp = malloc(strlen(buf) + 1);
	if (cp == NULL){
		fprintf(stderr, "no memory\n");
		exit(1);
	}
	strcpy(cp, buf);
	return cp;
}

下面是输出结果:

yubo@debian:~/test/tmp/unix/shell$ ./psh
Arg[0]ls
Arg[1]-l
Arg[2].
Arg[3]
总用量 24
-rwxr-xr-x 1 yubo yubo 6108  5月 18 18:01 exec
-rwxr-xr-x 1 yubo yubo 9440  5月 18 23:00 psh
-rw-r--r-- 1 yubo yubo 1336  5月 18 23:00 psh2.c
child exited with status 0, 0
Arg[0]ps
Arg[1]
  PID TTY          TIME CMD
 3393 pts/1    00:00:00 bash
 3940 pts/1    00:00:00 psh
 3944 pts/1    00:00:00 ps
child exited with status 0, 0
Arg[0]

但是,有一点是比较遗憾的:

./psh
Arg[0]tr
Arg[1][a-z]
Arg[2][A-Z]
Arg[3]
hello
HELLO
now to press
NOW TO PRESS
^C
yubo@debian:~/test/tmp/unix/shell$

针对tr程序(psh的子进程)的Ctrl+C将psh也给停止了,正确的做法是不影响父进程。


上一篇 icmp分析

Comments

Content