本章討論進程概念、資源、屬性。程序員
當系統啓動時,內核代碼被加載到內存,初始化以後,啓動第一個用戶進程,而後內核的代碼就等着用戶進程來調度了。數組
當程序員編寫好一個程序,編譯以後會生成這個可執行程序,這個程序能夠被運行。安全
運行程序實際上是用戶進程(Shell進程)指示內核要啓動另外一個用戶進程,內核便爲這個新的進程分配資源,並加載該進程的代碼和數據。性能優化
一個程序能夠被運行屢次。函數
進程運行時,內核爲進程每一個進程分配一個PCB(進程控制塊),描述進程的信息。性能
PCB在內核中對應的結構體是task_struct
。優化
每一個進程都會分配虛擬地址空間,在32位機器上,該地址空間爲4G。
ui
更細節的圖例
spa
在進程裏平時所說的指針變量,保存的就是虛擬地址。當應用程序使用虛擬地址訪問內存時,處理器(CPU)會將其轉化成物理地址。操作系統
這樣作的好處在於:
進程隔離,更好的保護系統安全運行
屏蔽物理差別帶來的麻煩,方便操做系統和編譯器安排進程地址
思考:若是實現一個智能的myfree函數,該函數會自動判斷指針是否在堆上仍是在棧上,仍是在全局變量中。
CPU的分配是動態的,不是進程一加載就直接分配的,通常來講每一個系統都會有許多進程同時在運行,而CPU只有一個(多核CPU能夠認爲是多個,可是數量遠少於進程數量)。那麼,進程就須要排隊等待,就好像有100我的,在4個賣飯的窗口買飯同樣。
內核將進程PCB放入一個隊列,老是讓CPU服務隊列中的第一個進程,服務時間能夠是10毫秒,能夠是25毫秒,具體多長時間跟具體系統有關係,這個時間有個名字叫作時間片。一旦這個進程服務時間到,這個進程會被丟到隊列尾部,進行排隊。進程調度。
內核中有一個常量HZ,通常是100,250, 1000
進程有許多的屬性和狀態,具體能夠看task_struct,這裏挑一些常見的進行講解。
進程編號,內核爲每一個進程分配一個進程編號,這個是進程的身份證,系統保證了不會重複分配。
經過函數getpid
或者命令ps
能夠查看進程的PID。
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
PPID就是父進程ID,在Linux系統中,除了內核啓動的第一個進程,其它進程都有父進程。
經過函數getppid
或者命令ps
能夠查看進程的PPID。
帳戶分實際帳戶和有效帳戶兩種,若是你使用test帳戶登錄系統,可是使用sudo運行程序時,實際帳戶時test,有效帳戶時root。
經過函數getuid
和geteuid
獲取真實帳戶id和有效帳戶id
經過函數getgid
和getegid
得到真實帳戶id和有效帳戶id
經過setuid
,setgid
,seteuid
,setegid
,setreuid
,setregid
等設置進程的有效和真實帳戶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); }
進程組:getpgrp
和setpgid
會話組:getsid
和setsid
控制終端:
保存該進程運行的環境信息。
進程的環境變量保存在全局變量environ
中,
也能夠經過setenv
、getenv
、unsetenv
進行設置和獲取。
啓動進程時,該進程在RUNNING狀態,RUNNING狀態的進程有可能時正在被執行,或者在隊列中排隊。可是若是進程調用阻塞函數,而運行條件不知足時,該進程會進入掛起狀態。掛起狀態的進程再也不分配CPU,除非等到運行條件知足時。會阻塞進程運行的函數有許多,好比getchar是典型的阻塞調用。
阻塞函數列表能夠在man 7 signal
中,找到關於阻塞函數的列表。
在進程控制塊中,有一個數組保存着打開的文件描述符信息。
進程有一些字段,用來記錄進程的運行時間。
經過times
能夠獲取進程從運行開始時到執行times
函數時,所花費的時間。這個在系統性能優化時特別重要。
簡單的程序能夠從time
命令獲取進程的運行時間。
Linux時間相關函數能夠從man 7 time
獲取。
當前工做目錄是相對地址的相對目錄,經過getcwd
函數能夠獲取當前目錄,也能夠經過pwd
或者echo $PWD
獲取。也能夠經過chdir
來修改當前工做目錄。
根目錄是絕對地址的相對目錄,能夠經過chroot
來修改根目錄。調用chroot
須要root權限。
目錄相關資料在man 7 path_resolution
。
當使用動態庫時,系統會檢查該動態庫是否已經加載,若是已經加載,則直接映射便可,若是沒有加載,那麼會加載以後再映射。
若是動態庫中有全局變量,那麼該全局變量對於不一樣的進程來講,是相互獨立和隔離的。
連接靜態庫時,靜態庫被一塊兒編譯進可執行程序,運行時再也不依賴靜態庫。
動態庫編譯:gcc -fpic -shared a.c b.c -o libtest.so
連接動態庫gcc main.c -ltest -L. -o mybin
運行程序時
靜態庫打包:ar rcs libtest.a a.o b.o
連接庫時,若是有同名的動態庫和靜態庫,默認優先動態庫,若是要連接靜態庫,那麼使用-static
,好比
經過如下方式能夠指定某些庫使用靜態連接,而某些庫使用動態連接
進程運行時,老是佔用內存,不管是加載代碼,仍是在函數中定義局部變量,仍是調用malloc申請內存。
不管是那種緣由,進程須要使用內存時,它將向系統申請,並得到相對應的虛擬地址,而進程只能訪問虛擬地址,真實的內存地址,進程沒法訪問。當進程訪問虛擬地址時,系統會負責進行虛擬地址到物理地址的轉換,系統發現進程嘗試訪問非法地址,那麼進程將獲得懲罰(段錯誤)。
這樣作保護了系統的穩定性,不會由於個別新手程序員致使整個系統的崩潰。
另外還有一個好處是,使用虛擬內存以後,每一個進程的致使空間是一致的,簡化了進程的設計。
相關函數:malloc
,brk
,mmap
,alloca
從用戶的角度看,一個程序跑起來就是進程。而從操做系統的角度看,進程是一個控制塊+代碼+數據的組合。
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:在棧上申請內存
ps axu:現行終端機下的全部程序,以用戶爲主的格式來顯示程序情況,顯示全部程序,不以終端機來區分ps ajxgrep:搜索kill:殺死進程(給進程發送信號)