一、Unix shell的功能 shell
shell是一個管理進程和運行程序的程序。全部經常使用的shell都有3個主要功能: 編程
(1)運行程序; 數據結構
(2)管理輸入和輸出 編程語言
(3)可編程 函數
shell同時也是帶有變量和流程控制的編程語言。 this
二、Unix的進程模型 code
一個程序是存儲在文件中的機器指令序列,通常它是由編譯器將源代碼編譯成二進制格式的代碼。運行一個程序意味着將這些機器指令序列載入內存而後讓處理器(CPU)逐條執行。在Unix術語中,一個可執行程序是一些機器指令機器數據的序列。一個進程是程序運行時的內存空間和設置。數據和程序存儲在磁盤文件中,程序在進程中運行。 orm
每一個進程都有一個能夠惟一標識它的數字,被稱爲進程ID,通常簡稱PID;同時也有一個父進程ID(PPID)。每一個進程都與一個終端相連,都一個已運行的時間,有優先級,有niceness級別,有大小。。。 token
Unix系統中的內存分爲系統空間和用戶空間。進程存在於用戶空間。 進程
三、如何執行一個程序
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; }