標籤:
1. Linux進程概述
進程是一個程式一次執行的過程,它和程式有本質區別。程式是靜態,它是一些儲存在磁碟上的指令的有序集合;而進程是一個動態概念,它是一個運行著的程式,包含了進程的動態建立、調度和消亡的過程,是Linux的基本調度單位。那麼從系統的角度看如何描述並表示它的變化呢?在這裡,是通過進程式控制制塊(PCB)來描述的。進程式控制制塊包含了進程的描述資訊、控制資訊以及資源資訊,它是進程的一個靜態描述。
核心使用進程來控制對CPU和其他系統資源的訪問,並且使用進程來決定在CPU上運行哪個程式,運行多久以及採用什麼特性運行它。核心的調度器負責在所有的進程間分配CPU執行時間,稱為時間片(time slice),它輪流在每個進程分得的時間片用完後從進程那裡搶回控制權。
1.1. 進程標識
OS會為每個進程分配一個唯一的整型ID,做為進程的標識號(pid)。進程除了自身的ID外,還有父進程ID(ppid),所有進程的祖先進程是同一個進程,它叫做init進程,ID為1,init進程是核心自舉後的一個啟動的進程。init進程負責引導系統、啟動守護(後台)進程並且運行必要的程式。
進程的pid和ppid可以分別通過函數getpid()和getppid()獲得。
樣本:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main()
{
printf("pid:%d ppid:%d\n",getpid(),getppid());
return 0;
}
1.2. 進程的使用者ID與組ID(進程的運行身份)
進程在運行過程中,必須具有一類似於使用者的身份,以便進行進程的許可權控制,預設情況下,哪個登入使用者運行程式,該程式進程就具有該使用者的身份。例如,假設當前登入使用者為gotter,他運行了ls程式,則ls在運行過程中就具有gotter的身份,該ls進程的使用者ID和組ID分別為gotter和gotter所屬的組。這類型的ID叫做進程的真實使用者ID和真實組ID。真實使用者ID和真實組ID可以通過函數getuid()和getgid()獲得。
與真實ID對應,進程還具有有效使用者ID和有效組ID的屬性,核心對進程的存取權限檢查時,它檢查的是進程的有效使用者ID和有效組ID,而不是真實使用者ID和真實組ID。預設情況下,使用者的(有效使用者ID和有效組ID)與(真實使用者ID和真實組ID)是相同的。有效使用者id和有效組id通過函數geteuid()和getegid()獲得。
樣本
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main()
{
printf("uid:%d gid:%d euid:%d egid:%d\n",getuid(),getgid(),geteuid(),getegid());
return 0;
}
shell>id
uid=500(ghaha) gid=500(ghaha) groups=500(ghaha)
編譯產生可執行檔a.out,程式檔案的屬性可能為:
-rwxrwxr-x 1 ghaha ghaha 12132 Oct 7 09:26 a.out
執行結果可能為:
shell>./a.out
uid:500 gid:500 euid:500 egid:500
現在將a.out的所有者可執行屬性改為s
shell>chmod u+s a.out
shell>ll
-rwsrwxr-x 1 ghaha ghaha 12132 Oct 7 09:26 a.out
此時改另外一個使用者gotter登入並運行程式a.out
shell>id
uid=502(gotter) gid=502(gotter) groups=502(gotter)
shell>./a.out
uid:502 gid:502 euid:500 egid:502
可以看到,進程的有效使用者身份變為了ghaha,而不是gotter了,這是因為檔案a.out的存取權限的所有者可執行為設定了s的屬性,設定了該屬性以後,使用者運行a.out時,a.out進程的有效使用者身份將不再是運行a.out的使用者,而是a.out檔案的所有者。
s許可權最常見的例子是
/usr/bin/passwd程式,它的許可權位為
shell>ll /usr/bin/passwd
-r-s--x--x 1 root root 16336 Feb 13 2003 /usr/bin/passwd
我們知道,使用者的使用者名稱和密碼是儲存在/etc/passwd(後來專門將密碼儲存在/etc/shadow,它是根據/etc/passwd檔案來產生/etc/shadow的,它把所有口令從/etc/passwd中移到了/etc/shadow中。這裡用到的是影子口令,它將口令檔案分成兩部分:/etc/passwd和/etc/shadow,此時/etc/shadow就是影子口令檔案,它儲存的是加密的口令,而/etc/passwd中的密碼全部變成x)下的。通過ls –l查看/etc/passwd這個檔案,你會發現,這個檔案普通使用者都沒有可寫的許可權,那我們執行passwd的時候確實能夠修改密碼,那麼這是怎麼回事呢?也就是說,任何一個使用者運行該程式時,該程式的有效身份都將是root(用普通身份去執行這個操作的時候,它會暫時得到檔案擁有者root的許可權),而這樣passwd程式才有許可權讀取/etc/passwd檔案的資訊。
我們也來實現以下passwd的功能,實現步驟如下:
1. 用touch建立一個a.txt輸入內容“hello” 類似於/etc/passwd
2. 寫一個程式1.c如下:
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <stdlib.h>
#include <errno.h>
int main()
{
printf("uid : %d gid : %d\n", getuid(), getgid());
printf("eudi: %d egid: %d\n", geteuid(), getegid());
FILE* fp = fopen("a.txt", "a");//注意這裡是以追加形式開啟,說明a.txt要具有可寫入權限
if(fp == NULL)
{
perror("fopen error");
exit(-1);
}
fputs("world", fp);
fclose(fp);
return 0;
}
3. 編譯gcc –o 1 1.c產生可執行程式1 此時1類似於/usr/bin/passwd檔案
4. 用ll查看a.txt發現許可權是-rw-r—r—的許可權(跟/etc/passwd許可權一樣),說明普通使用者沒有可寫入權限,如果直接切換到普通使用者執行./1會報錯。
5. 用root身份將1的許可權修改為-rwsr-xr-x (操作命令:chmod u+s 1,此時跟/usr/bin/passwd的許可權一樣),此時再切換到普通使用者wangxiao,執行./1發現可以執行成功,因為此時./1進程的有效使用者id變成root了,也就是說普通使用者是藉助root身份來實現的。
1.3. 進程的狀態
進程是程式的執行過程,根據它的生命週期可以劃分成3種狀態。
l 執行態:該進程正在運行,即進程正在佔用CPU。
l 就緒態:進程已經具備執行的一切條件,正在等待分配CPU的處理時間片。
l 等待態:進程不能使用CPU,若等待事件發生(等待的資源分派到)則可將其喚醒。
1.4. Linux下的進程結構
Linux系統是一個多進程的系統,它的進程之間具有並行性、互不干擾等特點。也就是說,進程之間是分離的任務,擁有各自的權利和責任。其中,每個進程都運行在各自獨立的虛擬位址空間,因此,即使一個進程發生了異常,它也不會影響到系統的其他進程。
Linux中的進程包含3個段,分別為“資料區段”、“程式碼片段”和“堆棧段”。
· “資料區段”放全域變數、常數以及動態資料分配的資料空間。資料區段分成普通資料區段(包括可讀可寫/唯讀資料區段,存放靜態初始化的全域變數或常量)、BSS資料區段(存放未初始化的全域變數)以及堆(存放動態分配的資料)。
· “程式碼片段”存放的是程式碼的資料。
· “堆棧段”存放的是子程式的返回地址、子程式的參數以及程式的局部變數等。
1.5. Linux下的進程管理
啟動進程:手工啟動 調度啟動
備忘:
進程process:是os的最小單元 os會為每個進程分配大小為4g的虛擬記憶體空間,其中 1g給核心空間 3g給使用者空間{代碼區資料區 堆棧區}
ps查看活動進程 ps –aux查看所有的進程 ps -aux| grep ‘aa‘尋找指定(aa)進程 ps –ef可以顯示父子進程關係 top顯示前20條進程,動態改變 pgrep ‘vi‘尋找進程
進程狀態:執行 就緒 等待狀態
ps -aux看%cpu(cpu使用量) %mem(記憶體使用量量) stat狀態{S睡眠 T暫停 R運行 Z殭屍}
vi a.c &(&表示後台運行),一個死迴圈,按ctrl+z可以把進程暫停,再執行[bg作業ID]可以將該進程帶入後台。利用jobs可以查看背景工作,fg 1把背景工作帶到前台,這裡的1表示作業ID
kill -9 進程號è表示向某個進程發送9號訊號,從而殺掉某個進程 利用pkill a可以殺死進程名為a的進程
Linux進程式控制制(一)