Linux System Programming 學習筆記(五) 進程管理

1. 進程是unix系統中兩個最重要的基礎抽象之一(另外一個是文件)

A process is a running program
A thread is the unit of activity inside of a process
 
the virtualization of memory is associated with the process, the threads all share the same memory address space
 

2. pid

The idle process has the pid 0
The first process that the kernel executes after booting the system, called the init process, has the pid 1
 
默認狀況下,內核規定一個最大的進程ID值:32768(16位有符號數)
內核使用 嚴格的線性方式爲進程分配PID,若是當前全部分配的PID值最大爲17,那麼下一次分配的PID值爲18
進程層級:每個進程(init進程除外)都是從另外一個進程引生的
Children normally belong to the same process groups as their parents
 

3. 運行新進程

In Unix, the act of loading into memory and executing a program image is separate from the act of creating a new process
exec system call:loads a binary program into memory, replacing the previous contents of the address space, and begins execution of  the new program
fork system call:create a new process
to execute a new program in a new process :first a fork to create a new process, and then an exec to load a new binary into that process
 
exec系列函數中, 只有execve是系統調用,其他函數都是根據execve進行封裝的C語言庫函數
 
fork系統調用以後,子進程與父進程幾乎徹底相同,除了如下:
a.進程ID不一樣
b.掛起的信號被清除,並不被子進程繼承
c.父進程的文件鎖不被子進程繼承
 
This 「fork plus exec」 combination is frequent and simple:
 
pid_t pid = fork();
if (pid == -1) {
    fprintf(stderr, "fork error\n");
}

/* the child */
if (pid == 0) {
    const char* args[] = {"grep", NULL};
    int ret = execv("bin/grep", args);
    if (ret == -1) {
        fprintf(stderr, "execv error\n");
        exit(EXIT_FAILURE);
    }
}

 

4. copy-on-write

早期Unix中,實現fork很是簡單,the kernel created copies of all internal data structures, duplicated the process's page table entries, and then performed a
page-by-page copy of the parent's address space into the child's new address space, 這樣拷貝實現無疑是費時的
 
copy-on-write是一種減少資源複製開銷的延遲策略。
若是多個用戶請求對它所擁有的資源進行讀操做,資源副本的拷貝是不須要進行的,每一個用戶只須要操做一個指向共同資源的指針便可。只要沒有用戶試圖修改這個資源,那麼副本的拷貝開銷就能夠避免。若是有一個用戶確實須要修改這個資源,那麼直到這個時候,資源才複製一份,複製的一份資源被交給須要修改的用戶,其他全部用戶繼續共享原初的資源
 
在虛擬內存的具體示例中,copy-on-write是之內存頁爲單位進行的。
進程的內存頁首先被標記爲 read-only and copy-on-write, 若是有進程須要修改這個內存頁,就產生頁錯誤,就在這時,內核複製該內存頁,而且內存頁的copy-on-write標記被清除。
現代機器體系結構都硬件層面上支持copy-on-write
 

5. exit

當一個進程終止時,內核清除它包含的全部資源:分配的內存、打開的文件、信號量等
結束一個程序最經典的方式不是顯式調用exit,而是 "falling off the end" of the program
 
當一個進程終止時, 內核向其父進程發送 SIGCHLD 信號。默認狀況下,該信號被忽略,父進程也能夠調用 sigaction 捕捉該信號
 

6. wait

#include <sys/types.h>
#include <sys/wait.h>

/* wait() returns the pid of a terminated child or −1 on error. */
pid_t wait (int *status);


pid_t waitpid (pid_t pid, int *status, int options);
當pid==-1時,waitpid等待任一子進程,等同於 wait,即 wait (&status) 等同於 waitpid (−1, &status, 0)
 

7. system

#define _XOPEN_SOURCE    /* if we want WEXITSTATUS, etc. */
#include <stdlib.h>
int system (const char *command);
It is common to use system() to run a simple utility or shell script, often with the explicit goal of simply obtaining its return value
If command is NULL, system() returns a nonzero value if the shell /bin/sh is available, and 0 otherwise
 
here is a sample implementation for system():
 
/* my_system - sync spwans and waits for the command
 * /bin/sh -c <cmd> .
 *
 * Return -1 on error of any sort, or the exit code from
 * the launched process. Does not block or ignore any signals.
 */

int my_system(const char* cmd)
{
    int status;
    pid_t pid = fork();
    if (pid == -1) {
        fprintf(stderr, "fork error\n");
        return -1;
    } else if (pid == 0) {
        const char* argv[4];
        argv[0] = "sh";
        argv[1] = "-c";
        argv[2] = "cmd";
        argv[3] = NULL;
        execv("bin/sh", argv);
       exit(-1);
    }
    if (waitpid(pid, &status, 0) == -1) {
        return -1;
    } else if (WIFEXITED(status)) {
        return WEXITSTATUS(status);
    }
    return -1;
}

 

8. 殭屍進程

a process that has terminated but has not yet been waited upon by its parent is called a 「zombie.」
Zombie processes continue to consume system resources, although only a small percentage
若是一個父進程fork產生了子進程,那麼該父進程就必須負責wait其子進程(回收殭屍子進程的所佔資源)
若是一個父進程在終止以前沒有wait其子進程呢? 內核會視察其全部子進程,並將這些子進程掛在init進程之下(確保任何進程在任什麼時候候都有一個父進程),init進程會週期性wait其全部子進程,這些子進程最終仍是會被回收資源,而不會長期以殭屍進程形式存在
 

9. Users and Groups

#include <sys/types.h>
#include <unistd.h>

int setuid (uid_t uid);
int setgid (gid_t gid);

uid_t getuid (void);
gid_t getgid (void);
Each process is a member of a process group,Process groups that contain more than one process are generally implementing job control
 
A command on the shell such as this one:
/*  one process group containing three processes */
$ cat ship-inventory.txt | grep booty | sort
 

10. 守護進程

A daemon is a process that runs in the background, not connecting to any controlling terminal
一般在系統啓動時開始運行,而且以root權限運行,處理系統級任務,名字後綴通常爲d
 
一個守護進程必須知足:a. 是init進程的子進程   b. 不容許與終端鏈接
相關文章
相關標籤/搜索