Linux第四章 進程

4.1 前言

本章討論進程概念、資源、屬性。程序員

4.2 內核和進程的關係

當系統啓動時,內核代碼被加載到內存,初始化以後,啓動第一個用戶進程,而後內核的代碼就等着用戶進程來調度了。數組

4.3 進程是程序的實例

當程序員編寫好一個程序,編譯以後會生成這個可執行程序,這個程序能夠被運行。安全

運行程序實際上是用戶進程(Shell進程)指示內核要啓動另外一個用戶進程,內核便爲這個新的進程分配資源,並加載該進程的代碼和數據。性能優化

一個程序能夠被運行屢次。函數

4.3 進程資源

4.3.1 PCB

進程運行時,內核爲進程每一個進程分配一個PCB(進程控制塊),描述進程的信息。性能

PCB在內核中對應的結構體是task_struct優化

4.3.2 虛擬地址空間

每一個進程都會分配虛擬地址空間,在32位機器上,該地址空間爲4G。
Snip20161008_19ui

更細節的圖例
Snip20161008_23spa

在進程裏平時所說的指針變量,保存的就是虛擬地址。當應用程序使用虛擬地址訪問內存時,處理器(CPU)會將其轉化成物理地址。操作系統

int* p = malloc(100);
*p = 100;
訪問內存時,系統會作地址轉換。

這樣作的好處在於:

  • 進程隔離,更好的保護系統安全運行

  • 屏蔽物理差別帶來的麻煩,方便操做系統和編譯器安排進程地址

思考:若是實現一個智能的myfree函數,該函數會自動判斷指針是否在堆上仍是在棧上,仍是在全局變量中。

4.3.3 CPU

CPU的分配是動態的,不是進程一加載就直接分配的,通常來講每一個系統都會有許多進程同時在運行,而CPU只有一個(多核CPU能夠認爲是多個,可是數量遠少於進程數量)。那麼,進程就須要排隊等待,就好像有100我的,在4個賣飯的窗口買飯同樣。

內核將進程PCB放入一個隊列,老是讓CPU服務隊列中的第一個進程,服務時間能夠是10毫秒,能夠是25毫秒,具體多長時間跟具體系統有關係,這個時間有個名字叫作時間片。一旦這個進程服務時間到,這個進程會被丟到隊列尾部,進行排隊。進程調度。

內核中有一個常量HZ,通常是100,250, 1000

4.4 進程屬性和狀態

進程有許多的屬性和狀態,具體能夠看task_struct,這裏挑一些常見的進行講解。

4.4.1 PID

進程編號,內核爲每一個進程分配一個進程編號,這個是進程的身份證,系統保證了不會重複分配。
經過函數getpid或者命令ps能夠查看進程的PID。

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

int main()
{
    pid_t pid = getpid();
    pid_t ppid = getppid();
    printf("%d\n", (int)pid);
    printf("%d, %d\n", (int)pid, (int)ppid);
}

4.4.2 PPID

PPID就是父進程ID,在Linux系統中,除了內核啓動的第一個進程,其它進程都有父進程。
經過函數getppid或者命令ps能夠查看進程的PPID。

4.4.3 帳戶ID/組ID

帳戶分實際帳戶和有效帳戶兩種,若是你使用test帳戶登錄系統,可是使用sudo運行程序時,實際帳戶時test,有效帳戶時root。

經過函數getuidgeteuid獲取真實帳戶id和有效帳戶id
經過函數getgidgetegid得到真實帳戶id和有效帳戶id
經過setuidsetgidseteuidsetegidsetreuidsetregid等設置進程的有效和真實帳戶id。

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

int main()
{
    uid_t uid = getuid();
    uid_t euid = geteuid();
    printf("uid =%d, euid=%d\n", (int)uid, (int)euid);
}

 

4.4.4 進程組ID/會話組ID/控制終端

進程組:getpgrpsetpgid
會話組:getsidsetsid
控制終端:

4.4.5 環境變量

保存該進程運行的環境信息。
進程的環境變量保存在全局變量environ中,
也能夠經過setenvgetenvunsetenv進行設置和獲取。

4.4.6 進程狀態

#define TASK_RUNNING            0 可運行狀態,至關於進程三種狀態的執行和就緒狀態
#define TASK_INTERRUPTIBLE      1 中斷等待狀態。處於這種狀態喚醒的緣由多是信號或定時中斷,或者I\O就緒
#define TASK_UNINTERRUPTIBLE    2 不可中斷等待狀態,主要是等待I\O
#define TASK_ZOMBIE             3 僵死狀態,進程已經結束已經釋放除了PCB之外的部分系統資源,等待父進程wait()讀取結束狀態
#define TASK_STOPPED            4 進程已經中止
注意:這是0.11的內核,在1.0的內核以上就多了一種狀態,在1.0內核的sched.h中有定義
#define TASK_SWAPPING           5 交換狀態,進程的頁面也能夠從內存轉換到外存,以空出內存空間

啓動進程時,該進程在RUNNING狀態,RUNNING狀態的進程有可能時正在被執行,或者在隊列中排隊。可是若是進程調用阻塞函數,而運行條件不知足時,該進程會進入掛起狀態。掛起狀態的進程再也不分配CPU,除非等到運行條件知足時。會阻塞進程運行的函數有許多,好比getchar是典型的阻塞調用。

阻塞函數列表能夠在man 7 signal中,找到關於阻塞函數的列表。

 

4.4.7 文件描述符

在進程控制塊中,有一個數組保存着打開的文件描述符信息。

4.4.8 進程時間

進程有一些字段,用來記錄進程的運行時間。
經過times能夠獲取進程從運行開始時到執行times函數時,所花費的時間。這個在系統性能優化時特別重要。
簡單的程序能夠從time命令獲取進程的運行時間。
Linux時間相關函數能夠從man 7 time獲取。

 

4.4.10 當前工做目錄和根目錄

當前工做目錄是相對地址的相對目錄,經過getcwd函數能夠獲取當前目錄,也能夠經過pwd或者echo $PWD獲取。也能夠經過chdir來修改當前工做目錄。
根目錄是絕對地址的相對目錄,能夠經過chroot來修改根目錄。調用chroot須要root權限。

目錄相關資料在man 7 path_resolution

4.5 動態庫和靜態庫

當使用動態庫時,系統會檢查該動態庫是否已經加載,若是已經加載,則直接映射便可,若是沒有加載,那麼會加載以後再映射。

若是動態庫中有全局變量,那麼該全局變量對於不一樣的進程來講,是相互獨立和隔離的。

連接靜態庫時,靜態庫被一塊兒編譯進可執行程序,運行時再也不依賴靜態庫。

動態庫編譯:
gcc -fpic -shared a.c b.c -o libtest.so
連接動態庫
gcc main.c -ltest -L. -o mybin
運行程序時

export LD_LIBRARY_PATH=.
或者將動態庫拷貝到/usr/lib
./mybin

靜態庫打包:
ar rcs libtest.a a.o b.o

連接庫時,若是有同名的動態庫和靜態庫,默認優先動態庫,若是要連接靜態庫,那麼使用-static,好比

gcc a.c -lmylib -static

經過如下方式能夠指定某些庫使用靜態連接,而某些庫使用動態連接

-Wl,-Bstatic -ltest -Wl,-Bdynamic -ltest2

4.6 內存管理

進程運行時,老是佔用內存,不管是加載代碼,仍是在函數中定義局部變量,仍是調用malloc申請內存。

不管是那種緣由,進程須要使用內存時,它將向系統申請,並得到相對應的虛擬地址,而進程只能訪問虛擬地址,真實的內存地址,進程沒法訪問。當進程訪問虛擬地址時,系統會負責進行虛擬地址到物理地址的轉換,系統發現進程嘗試訪問非法地址,那麼進程將獲得懲罰(段錯誤)。

這樣作保護了系統的穩定性,不會由於個別新手程序員致使整個系統的崩潰。

另外還有一個好處是,使用虛擬內存以後,每一個進程的致使空間是一致的,簡化了進程的設計。

相關函數:mallocbrkmmapalloca

4.7 進程總結

從用戶的角度看,一個程序跑起來就是進程。而從操做系統的角度看,進程是一個控制塊+代碼+數據的組合。

4.8 函數和命令

4.8.1 函數

getpid:獲取進程ID
getppid:獲取父進程ID

getuid:獲取實際用戶ID
getgid:獲取實際組ID
geteuid:獲取有效帳戶ID
getegid:獲取有效組ID

進程組描述了一項任務
getpgrp:獲取進程組號
setpgid:設置進程組號

setsid:設置Session號
getsid:得到Session號

getcwd:獲取當前工做目錄
chdir:設置當前工做目錄
chroot:修改當前根目錄

getenv:環境中取字符串,獲取環境變量的值
setenv:改變或增長環境變量
unsetenv
extern char** environ(全局變量)

malloc/free:堆區申請內存
mmap/munmap:在映射區申請內存
brk:全局區申請內存
alloca:在棧上申請內存

int foo(int len)
{
//  char buf[len];
    char* buf = alloca(len);
}

4.8.2 命令

ps axu:現行終端機下的全部程序,以用戶爲主的格式來顯示程序情況,顯示全部程序,不以終端機來區分ps ajxgrep:搜索kill:殺死進程(給進程發送信號)

相關文章
相關標籤/搜索