Linux之進程

1.進程的定義

在討論進程定義之前,我們可以先來了解一下計算機操作系統爲什麼要引入進程這個概念。
1.1 進程的引入
當計算機系統只有一個程序運行時,稱之爲單道程序,此時這個程序獨佔系統中的所有資源,在執行的過程中不受外界的影響;而多道程序在執行時,就是所謂的程序併發執行,即若干個程序同時在系統中執行,這時,這些程序就不可能獨佔所有的系統資源了,而需要多個程序共享系統的資源,從而導致各個程序在執行時出現相互制約的關係。爲了刻畫系統內部出現的這種動態情況,描述程序併發執行的活動規律,操作系統就引入了進程這個概念,進程的出現是爲了使多個程序併發執行,用以改善資源利用率,並且提高系統的吞吐量。
1.2 進程的定義
進程是一個“執行中的程序”,即程序在處理機上執行時所發生的活動。
由上述簡單的定義可以發現,進程與程序有着密不可分的關係,運行中的程序在內存中的映像就叫做進程。
1.3查看進程信息
在Windows系統下,可以通過任務管理器來查看系統中的進程信息。
在Linux系統下,可以通過命令 “ps -aux”來查看系統中的進程信息。如下圖所示:
在這裏插入圖片描述

2.進程的基本操作

2.1 進程創建
在Linux下,提供了好幾個關於創建進程的操作函數,如fork(), vfork()
(1)fork()函數
該函數的功能是創建一個進程,新進程爲當前進程的子進程,當前進程就被稱爲父進程。該函數的調用形式爲:pid_t fork(void);使用該函數要引用頭文件<sys/types.h>和<unistd.h>頭文件,函數返回值類型爲pid_t,表示一個非負整數。若程序運行在父進程中,函數返回的PID爲子進程的進程號;若運行在子進程中,返回的PID爲0.若創建子進程失敗,則會返回-1.
程序代碼如下:

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

int main()
{
    pid_t pid;
    pid = fork();
    if(pid <0)
    {
	printf("fork error!\n");
	exit(1);
    }
    else if(pid == 0)
    {
	printf("in the child process!\n");
    }
    else
    {
	printf("in the parent process!\n");
    }
    exit(0);
}

從程序的結果可以看出fork()函數的一個特點,即“調用一次,返回兩次”,這又是爲什麼呢?
原來,在一個程序中,調用fork()函數後,就出現了分叉。在子進程中,fork()函數返回0;在父進程中,fork()函數返回子進程的PID。但是,調用fork()之後,誰先執行完全由調度器決定。
(2)vfork()函數
與fork()函數相同,這兩個函數都是系統調用函數。而兩者的區別是在創建子進程時fork()函數會複製父進程的所有資源,包括進程環境、內存資源等。而vfork()函數在創建子進程時並不會複製父進程的所有資源,父子進程共享地址空間。這樣,在子進程中對虛擬地址空間中的變量修改,實際上是在修改父進程虛擬地址空間中的值。
以下是個實例,演示兩個函數的區別。

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

int gvar = 2;

int main()
{
    pid_t pid;
    int var = 5 ;
    printf("process id: %ld\n", (long)getpid());
    printf("gvar = %d var = %d\n",gvar, var);
    pid = vfork();
    if(pid<0)
    {
	perror("error!\n");
	return 1;
    }
    else if(pid ==0)
    {
	gvar--;
	var++;
	printf("the child process id: %ld\ngvar = %d var=%d\n",(long)getpid(),gvar,var);
	_exit(0);
    }
    else
    {	
	printf("the parent process id: %ld\ngvar = %d var=%d\n",(long)getpid(),gvar,var);
	return 0;
    }
}

結果如下:
在這裏插入圖片描述
由運行結果可以看出,父進程中輸入的變量值也是在子進程中變化後的值。由此可知,調用vfrok()函數改變子進程中的值,其實就是改變父進程中的值。

2.進程等待

進程等待就是爲了同步父進程與子進程,通常需要通過wait()函數使父進程等待子進程結束。如果父進程沒有調用等待函數,子進程就會進入“殭屍(Zombie)”狀態。
Linux提供的等待函數原型如下:

#include<sys/types.h>
#include<sys/wait.h>
pid_t wait(int* status);
pid_t waitpid(pid_t pid,int* status, int options);

wait()函數系統調用的工作過程是:首先判斷子進程是否存在,即是否成功創建了一個子進程。如果創建失敗,子進程不存在,則會直接退出進程,並且提示相關錯誤信息;如果創建成功,那麼wait()函數會將父進程掛起,直到子進程結束,並且返回結束時的狀態和最後結束的子進程的PID。

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

/*定義一個功能函數,通過返回的狀態,判斷進程是正常退出還是信號導致的退出*/
void exit_s(int status)
{
    if(WIFEXITED(status))
    {
	printf("正常退出,status=%d\n",WEXITSTATUS(status));
    }
    else if(WIFSIGNALED(status))
    {
	printf("信號退出,status=%d\n",WTERMSIG(status));
    }
}

int main()
{
    pid_t pid, pid1;
    int status;
  //創建一個子進程
    if((pid=fork())<0)
    {
	printf("child process error!\n");
	exit(0);
    }
    else if(pid ==0) //子進程
    {
	printf("the child process!\n");
	exit(2);  //調用exit()退出函數正常退出
    }
    if(wait(&status)!=pid) //在父進程中調用wait()函數等待子進程結束
    {
	printf("This is a parent process!\nwait error!\n");
	exit(0);
    }
    exit_s(status);  //wait()函數調用成功,調用自定義的功能函數來判斷退出類型

    /*又一次創建子進程,在子進程中,使用kill()函數發送信號,導致退出*/
    if((pid=fork()) <0)
    {
	printf("child process error!\n");
	exit(0);
    }
    else if(pid == 0)
    {
	printf("the child process!\n");
	pid1 = getpid();
	/*使用kill()函數發送信號*/
	kill(pid1,19);
    }
    if(wait(&status)!=pid1)
    {
	printf("This is a parent process!\nwait error!\n");
	exit(0);
    }
    exit_s(status);
    exit(0);
}

提示:在Linux系統的終端輸入“kill -l”命令,可以列出信號的具體情況、信號類型和其所對應的數字。

關於進程等待函數,還有一個常用的等待函數waitpid(),該函數實現的功能與wait()函數相同,但它們區別在於:wait()函數用於等待所有子進程的結束,而waitpid()函數僅用於等待某個特定進程的結束,這個特定的進程是指其pid與函數中的參數pid相關時。所謂的相關,有如下幾種可能:
(1) pid=-1,等待任一個子進程,與wait()等效
(2)pid>0, 等待其進程ID與pid相等的子進程。

3.進程結束

當想要終止或者結束一個進程時,會使用系統調用exit()函數正常退出進程。該系統調用包括exit()和_exit()兩個函數。
1.exit()函數
原型爲:

 #include <stdlib.h>
  void exit(int status);

注意:該函數調用成功與失敗都沒有返回值,並且沒有出錯信息的提示。
exit()函數的作用是終止進程,並將運算status&0377表達式後的值返回給父進程,在父進程中通過wait()函數來獲得。
2._exit()函數
原型爲:

 #include <stdlib.h>
void exit(int status);

同上,無論成功與否,都沒有返回值。
在之前的進程創建時,vfork()函數創建的子進程在退出時只能使用_exit()函數退出進程,而不能使用exit()函數來退出。這是因爲在調用exit()函數時,會對輸入/輸出流進行刷新,釋放所佔用的資源以及清空緩衝區等;而 _exit()函數則不具備刷新緩衝區等操作的功能。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章