1、Unix shell的功能
shell是一个管理进程和运行程序的程序。所有常用的shell都有3个主要功能:
(1)运行程序;
(2)管理输入和输出
(3)可编程
shell同时也是带有变量和流程控制的编程语言。
2、Unix的进程模型
一个程序是存储在文件中的机器指令序列,一般它是由编译器将源代码编译成二进制格式的代码。运行一个程序意味着将这些机器指令序列载入内存然后让处理器(CPU)逐条执行。在Unix术语中,一个可执行程序是一些机器指令机器数据的序列。一个进程是程序运行时的内存空间和设置。数据和程序存储在磁盘文件中,程序在进程中运行。
每个进程都有一个可以唯一标识它的数字,被称为进程ID,一般简称PID;同时也有一个父进程ID(PPID)。每个进程都与一个终端相连,都一个已运行的时间,有优先级,有niceness级别,有大小。。。
Unix系统中的内存分为系统空间和用户空间。进程存在于用户空间。
3、如何执行一个程序
shell打印提示符,用户输入指令,shell就运行这个命令,然后shell再次打印提示符——如此反复。
一个shell的主循环执行下面的4步:
(1)用户键入a.out
(2)shell建立一个新的进程来运行这个出现
(3)shell将程序从磁盘载入
(4)程序在它的进程中运行知道结束
即:
while (!end_of_input)
get command
execute command
wait for command to finish
man 3 exec
#include <unistd.h>
extern char **environ;
int execl(const char *path, const char *arg, ...);
int execlp(const char *file, const char *arg, ...);
int execle(const char *path, const char *arg,
..., char * const envp[]);
int execv(const char *path, char *const argv[]);
int execvp(const char *file, char *const argv[]);
int execvpe(const char *file, char *const argv[],
char *const envp[]);
/*
* The exec() family of functions replaces the current process
* image with a new process image. The functions described
* in this manual page are front-ends for execve(2).
*/
一个进程调用fork来复制自己。进程调用fork,当控制转移到内核中的fork代码后,内核做:
(1)分配新的内存块和内核数据结构
(2)复制原来的进程到新的进程
(3)向运行进程添加新的进程
(4)将控制返回给两个进程
man 2 fork
#include <unistd.h>
pid_t fork(void);
进程调用wait等待子程序的结束。系统调用wait做两件事。首先,wait暂停调用它的进程直到子进程介绍。然后,wait取得子进程结束时传给exit的值。这样wait执行两个操作:通知和通信。
wait的目的之一是通知父进程子进程结束运行了。它的第二个目的是告诉父进程子进程是如何结束的。一个进程以3中方式(成功、失败或死亡)之一结束。按照Unix惯例,成功的程序调用exit(0)或者从main函数中return 0;程序遇到问题而要退出调用exit时传给它一个非零的值。
父进程调用wait时传一个整型变量地址给函数。内核将子进程的u提出状态保存在这个变量中。如果子进程调用exit退出,那么内核把exit的返回值放到这个整型变量中;如果进程是被杀死的,那么内核将信号序号存放在这个变量中。这个整数由3部分组成——高8位记录退出值,低7位记录信号序号,第7位则用来指明发生错误并产生了内核映像(core dump)。
man 2 wait
#include <sys/types.h>
#include <sys/wait.h>
pid_t wait(int *status);
pid_t waitpid(pid_t pid, int *status, int options);
int waitid(idtype_t idtype, id_t id, siginfo_t *infop, int options);
exit是fork的逆操作,进程通过调用exit来停止运行。fork创建一个进程,exit删除一个进程。基本上是这样。
exit刷新所以的流,调用atexit和on_exit注册的函数,执行当前系统定义的其他与exit相关的操作。然后调用_exit。系统函数_exit是一个内核操作,这个操作处理所有分配给这个进程的内存,关闭所有这个进程打开的文件,释放所有内核用来管理和维护这个进程的数据结构。
那些已经死亡但是没有给exit赋值的进程被称为僵尸进程。
系统调用_exit终止当前进程并执行所有必须的清理工作:
(1)关闭所有文件描述符和目录描述符
(2)将该进程的PID置为init进程的PID
(3)如果父进程调用wait或waitpid来等待子进程结束,则通知父进程
(4)向父进程发送SIGCHLD信号
下图摘自书本,为shell的fork()、exec()和exit()循环
/*
* prompting shell version 02
*
* Solves the 'one-shot' problem of version 01
* Uses execvp(), but fork()s first so that the
* shell waits around to perform another command
* New problem: shell cathes signals. Run vi, press ^C.
*/
#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 main(void)
{
char *arglist[MAXARGS+1]; /* an array of ptrs */
int numargs; /* index into array */
char argbuf[ARGLEN]; /* read stuff here */
void execute(char **);
char *makestring(char *); /* 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; /* colse list */
execute(arglist); /* do it */
numargs = 0; /* and reset */
}
}
}
return 0;
}
void execute(char **arglist)
/*
* use fork and execvp and wiat to do it
*/
{
pid_t pid, exitstatus; /* of child */
pid = fork(); /* make new process */
switch (pid)
{
case -1:
perror("fork falued");
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);
break;
}
}
char *makestring(char *buf)
/*
* trim off newline and create storage for the string
*/
{
char *cp;
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;
}
来源:oschina
链接:https://my.oschina.net/u/562215/blog/297385