進程和程序:編寫shell——《Unix/Linux編程實踐教程》讀書筆記(第8章)

一、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


一個程序如何運行另外一個程序?答案是程序經過調用exec家族函數:
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和_exit

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;
}
相關文章
相關標籤/搜索