Linux(高級編程)1————進程概念

何爲進程?
進程的典型定義:
1.進程是程序的一次執行。
2.進程是一個程序及其數據在處理機上順序執行時所發生的活動。
3.進程是具有獨立功能的程序在數據集合上運行的過程,他是系統進行資源分配和調度的一個獨立單位。
進程是進程實體的運行過程,是系統進行資源分配和調度的一個獨立單位。
進程 = 程序段+數據段+PCB(程序控制塊)
那麼進程有什麼特徵呢?
1.進程具有動態性。在這一點我們可以從進程狀態可以看出,進程從產生–>執行->消亡的過程其實就是一個動態的過程。
2.進程具有併發性。是指多個進程同時存在內存中,且在一段時間內同時進行(是宏觀上的,因爲一個處理機同一時刻只能處理一個進程)。
3.進程具有獨立性。在傳統的OS中,獨立性是指進程實體是一個獨立運行、獨立獲得資源和獨立接受調度的基本單位。
4.進程具有異步性。指進程是按異步方式運行。
上面對進程定義及特點說了一大堆,但我們似乎並沒有意識到進程到底是個什麼東西?那麼我們來看一些東西實實在在感受一下進程面貌。
1.進程描述
在操作系統,進程是用一個結構體描述的(PCB),而在Linux操作系統中我們用的是task_struct,PCB的一種。
接下來我們來看一下task_struct中都有一些什麼東西呢(核心成員)?

  • 1.進程描述符(身份標識):pid(進程ID)
    a.內部表示符:在所有的操作系統中,都爲每一個進程賦予了一個惟一的數字標識符,它通常是一個進程的序號。設置內部標識符主要是爲了方便系統使用。
    b.外部標識符:它由創建者提供,通常是由字母,數字組成,往往是由用戶(進程)在訪問該進程時使用。爲了描述進程的家族關係,還應設置父進程標識和子進程標識。此外,還可設置用戶標識,以指示擁有該進程的用戶。

  • 2.一組內存指針:代碼和代碼依賴的數據(告訴進程對應的代碼和代碼依賴的數據)。

  • 3.輔助操作系統進行調度的屬性
    a.優先級
    b.上下文信息(CPU中的各種寄存器:通用寄存器、指令寄存器、程序狀態字PSW、用戶棧指針):保存該進程上次在CPU上執行的現場。
    c.記賬信息(進程切換時)指令執行的數量。
    d.進程狀態

  • 4.IO相關信息(文件描述符表)。

  • 5.信號相關的信息(後面詳細解釋)

2.進程的組織
進程通過PCB描述後,通過雙向鏈表組織起來。
3.如何創建一個進程?

  • fork()函數,用來創建子進程
    特點:
    1.子進程複製父進程以父進程爲模板(寫時拷貝)。
    2.把父進程的PCB複製過來並稍加修改(pid/ppid)。
    3.內存指針基本相同,上下文也相同(EIP)。
    4.父子進程執行同一份代碼。
    5.EIP相同,所以fork之後,父子進程都要從fork之後開始執行。
    fork返回的信息:
    1.父進程返回子進程ID。
    2.子進程返回0。
    3.fork執行失敗,返回-1。
  • 失敗原因:

1.內存不夠。
2.子進程數目達到上限。
fork之後,父子進程執行的先後順序取決於操作系統的調度。

  • vfork()函數
    vfork是linux前期的進城創建函數,其缺點比較多。
    特點:
    1.父子進程共享進程地址空間。
    2.父子進程執行順序固定,子進程先執行父進程後執行。

4.進程狀態:
1.創建狀態:進程在創建時需要申請一個空白PCB,向其中填寫控制和管理進程的信息,完成資源分配。如果創建工作無法完成,比如資源無法滿足,就無法被調度運行,把此時進程所處狀態稱爲創建狀態

2.就緒狀態:進程已經準備好,已分配到所需資源,只要分配到CPU就能夠立即運行

3.執行狀態:進程處於就緒狀態被調度後,進程進入執行狀態

4.阻塞狀態:正在執行的進程由於某些事件(I/O請求,申請緩存區失敗)而暫時無法運行,進程受到阻塞。在滿足請求時進入就緒狀態等待系統調用

5.終止狀態:進程結束,或出現錯誤,或被系統終止,進入終止狀態。無法再執行

  • 進程狀態轉換:

在這裏插入圖片描述

  • Linux內核關於進程狀態的描述:
    在這裏插入圖片描述
    殭屍進程:
    1.成因1:子進程執行完了,父進程沒有做一些特殊處理。
    2.危害:PCB沒有釋放(內存泄露)。
    3.處理:kill掉某個進程,發送一個特定信號就能完成進程銷燬。kill -9 pid殺死進程組。
    直接殺死殭屍進程是殺不掉的,但可以殺掉(kill 父進程)父進程和殭屍進程都銷燬。
    4.成因2:殭屍進程是子進程向父進程彙報工作結果的一種機制。在父進程查看結果前,這樣的結果不應該被釋放掉,結果仍保存子進程的PCB中,父進程獲取子進程結果的方式“進程等待” 。
    孤兒進程
    父進程結束,子進程沒有結束成爲孤兒進程。孤兒進程的父進程爲1號進程init,1號進程會釋放孤兒進程。
    —————————————————————————————————————————
    fork使用樣例:
#include<stdio.h>
#include<unistd.h>
#include<sys/types.h>
#include<stdlib.h>

int main(void)
{
	pid_t pid = 0;
	int i = 1;
	if((pid = fork())<0)
	{
		printf("fork error! i = %d\n",i);
		exit(0);
	}
	if(pid == 0)
	{
		printf("child! i = %d\n",i);
	}
	else
	{
		i = 2;
		printf("father! i = %d\n",i);
	}
	return 0;
}

運行結果:
在這裏插入圖片描述
通過運行結果我們可以驗證:父子進程有各自的地址空間。接下來再對比一下vfork();
vfork函數樣例:

#include<unistd.h>
#include<sys/types.h>
#include<stdlib.h>
#include<stdio.h>
int main(void)
{
	pid_t pid = 0;
	int i = 1;
	if((pid = vfork())<0)
	{
		printf("vfork error! i = %d\n",i);
		exit(0);
	}
	if(pid >  0)
	{
		printf("father! i = %d\n",i);
	}
	else 
	{
		i = 2;
		printf("child! i = %d\n",i);
		exit(1);
	}
	return 0;
}

運行結果:
在這裏插入圖片描述
我們可以清楚的看到:1.子進程先運行,父進程後運行。2.子進程對i進行更改,父進程的值也發生了變化,說明父子進程共享進程地址空間。
創建一個殭屍進程實例:

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>

int main(void)
{
	pid_t pid;
	if((pid = fork())<0)
	{
		printf("fork error!\n");
		exit(0);
	}
	if(pid > 0)
	{
		while(1);//父進程死循環
	}
	else
	{
		printf("child ID = %d\n",getpid());//父進程沒有wait子進程,子進程退出。
		exit(0);
	}
}

運行結果:
在這裏插入圖片描述
在這裏插入圖片描述
可以看到子進程退出,父進程沒有爲子進程收屍,導致子進程變爲殭屍進程。
kill掉父進程再查看還有沒有殭屍進程:
在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述
我們可以看到父進程殺死後,殭屍進程也隨之釋放。
孤兒進程實例:

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>

int main(void)
{
	pid_t pid;
	if((pid = fork())<0)
	{
		printf("fork error!\n");
		exit(0);
	}
	if(pid > 0)
	{
		exit(1);//父進程退出
	}
	else
	{
		printf("child ID = %d,father ID = %d\n",getpid(),getppid());
		while(1);//子進程死循環
	}
}

運行結果:
在這裏插入圖片描述
子進程在父進程退出後仍在運行,子進程成爲孤兒進程,父進程變爲1號進程。

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