進程(task)
一. 進程的基本概念
進程:程序的一個執行實例,正在執行的程序。
從內核上來看,進程擔當分配系統資源(CPU時間,內存)的實體。
從廣義上定義:進程是一個具有一定獨立功能的程序關於某個數據集合的一次運行活動。它是操作系統動態執行的基本單元,在傳統的操作系統中,進程既是基本的分配單元,也是基本的執行單元。
二. 進程控制塊PCB(task_struct)
進程控制塊是用來描述進程的,在它裏面存儲着這個進程的相關信息,所以是每一個進程都會有一個進程控制塊,並且在進程控制塊中存儲的信息自然也不會相同。
進程信息被放在進程控制塊中,進程控制塊是一個數據結構,是進程屬性的集合。課本上的進程控制塊被稱爲PCB,在Linux操作系統下的PCB叫做:task_struct。task_struct是linux內核中的一種數據結構,它會被裝載到RAM(內存)裏並且包含着進程的信息。
上面說到了PCB是進程所有屬性的集合。那麼PCB中包含哪些屬性呢?
1. 標示符:描述進程的唯一標誌信息,用來區分進程,每個進程的標示符都不同,相當與人的身份證號,用來區別人與人。
2. 狀態:當前進程的狀態,並且保存進程退出的信息等
3. 優先級:運行時的優先級,相對與其他進程。
4. 程序計數器:即EIP,指向程序中下一條即將被執行的指令。在CPU中有一份,在內存中也有一份。
5. 內存指針:包括程序代碼和進程相關數據的指針,還有和其他進程共享的內存塊的指針。
6. 上下文數據:進程執行時處理器的寄存器中的數據。
7. I/O狀態信息:包括顯示的I/O請求,分配給進程的I/O設備和被進程使用的文件列表
8. 記賬信息:包括處理器時間的總和,使用的時鐘數總和,時間限制,記賬號等。
9. 其他信息
三. 進程的管理
進程的組織:在操作系統中,所有運行的進程都以task_struct鏈表的形式存在內核中。
查看進程:進程的信息可以通過/proc系統文件查看
若是想查看PID爲1的進程信息,就需要查看 /proc/1 這個文件夾
具體如下:
這個是/proc文件中的內容,包含當前系統下的所有進程的信息。可以看到包含了好多的文件夾,在每一個文件夾下就是單個進程的信息了。
這個就是當前系統下一號進程的信息了。
在linux系統下,有一個指令,ps也可以用來查看進程的信息。查詢結果如下圖所示,列出了當前用戶所產生的進程的信息。也可以帶參數查詢其他進程的信息。
下面這個是top命令的作用,即時顯示進程的動態,如果不退出就可以一直查看當前進程的信息,是動態的。
我們來編寫一段代碼,試着查看一下進程。代碼如下:
#include <stdio.h>
#include <unistd.h>
int main()
{
while(1)
{
sleep(1);
}
return 0;
}
這段代碼很簡單,就是讓這個進程一直處在睡眠狀態中。我們再重新打開一個窗口,查看當前所有進程。如果是直接使用ps指令,查到的就是當前用戶的進程,所以這樣時候查不到的,需要使用 ps aux指令來查詢,但是用這個查詢的結果太多不好找到我們想要的,那麼我們就可以使用
ps aux | grep a.out | grep -v grep
grep是用來篩選的,grep -v是用來將當前行指令去除,否則查詢到的結果中會有當前指令產生的進程。這樣我們就可以找到我們剛纔寫的程序產生的進程了。
除了以上的辦法,在linux中我們還有一種辦法可以用來得到進程的標示符。就使用系統函數。在系統中,進程的標示符就是它的ID,通常叫PID,其父進程的ID是PPID。
獲取當前進程的ID的函數爲getpid(),得到父進程ID的函數爲getppid()。下面舉個例子來看看吧。
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
int main()
{
printf("pid : %d\n",getpid());
printf("ppid : %d\n",getppid());
return 0;
}
當前進程的ID爲44456,其父進程的ID爲36360。
四. 進程的創建
在linux下有一個系統函數 fork(),它是用來創建進程的,並且它有一個特殊的地方,就是它的返回值有兩個。在父進程中返回的值爲子進程的PID,在子進程中返回0。我們就可以通過這個來區分當前的進程是子進程還是父進程。
fork之後父進程與子進程共享代碼,數據不共享,數據存儲在不同的位置。代碼存儲採用了寫時拷貝的方法。在需要修改代碼時,纔將父進程的代碼拷貝出來再進行修改。下面來看一段代碼:
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
int main()
{
int ret = fork();
printf("proc : %d ,ret : %d\n",getpid(),ret);
sleep(1);
return 0;
}
ret用來接收返回值,getpid()用來獲取當前進程的pid,父子進程誰先調用並不確定,取決於操作系統調度器。在這裏,是父進程先調用。爲什麼呢?因爲父進程的返回值應該是大於0的,而子進程的返回值爲0。這樣我們就可以寫出這樣的代碼:
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
int main()
{
pid_t pid;
pid = fork();
if(pid < 0)
perror("fork!");
else if(pid == 0)
printf("child's pid = %d\n",getpid());
else if(pid >= 0)
printf("parent's pid = %d\n",getpid());
return 0;
}
用返回值來區分父子進程,讓其做自己的事情。