操作系統之進程概念

進程概念

進程是什麼:

  表面上來說進程是程序的一個執行實例,或者是一個正在執行的程序等,從操作系統的角度來說,程序運行需要將代碼數據加載到內存中,由於在操作系統中運行了很多的程序,操作系統就必須去管理這些運行的程序,先描述再組織,所以在操作系統的層面的進程就是操作系統對一個運行的程序的描述。這個描述信息就是PCB(進程控制塊),在Linux中這個PCB有一個專屬的名稱task_struct結構體。task_struct是Linux內核的一種數據結構,他會被裝載到RAM裏,並且包含着進程的信息。
pcb中的描述信息

內存指針:包括程序代碼和進程相關的數據的指針,還有和其他進程共享的內存塊的指針。

程序計數器:程序中即將被執行的下一條指針的地址。

上下文數據:進程執行時處理器的寄存器中的數據。

標識符PID:描述本進程的唯一標識符,用來區別其他的進程。

進程狀態:任務狀態,退出代碼、退出信號等。

進程優先級:決定了cpu給進程分佈分配資源的優先級

記賬信息:處理器時間總和,使用的時鐘數總和,時間限制、記賬號等。

IO信息:包括顯示的I/O請求,分配給進程I/O設備和被進程使用的文件列表。

CPU分時機制

  對程序運行處理進行切換調度處理(時間片:cpu在每個程序上所運行的這段時間 )

查看進程

s -aux | grep 進程名
cd / -> cd /proc ->cd 進程ID

創建進程

創建子進程的意義:

分攤任務處理壓力,讓子進程完成其他任務(提高穩定性)

  操作系統通過寫時拷貝技術來創建子進程,也就是說通過複製父進程創建子進程,子進程初始時與父進程指向同一塊物理內存區域,當內存數據發生改變時,爲了保證進程之間的獨立性,操作系統會爲子進程重新開闢一段物理內存空間,並且更新子進程的頁表。

  複製:操作系統通過複製父進程創建子進程,因此父子進程數據獨有,但是代碼共享

  返回值:對於父進程來說,fork返回的是子進程的pid;創建子進程失敗返回-1,對於子進程來說,fork的返回值是0

用戶通過fork的返回值不同對父子進程進程運行流程進行分流

#include<iostream>
#include<string>
#include<pthread.h>
#include <unistd.h>
#include<vector>
using namespace std;

int main()
{

    //pid_t fork(void)
    //創建一個子進程,父進程返回子進程的pid,子進程返回0
    pid_t pid = fork();

    if(pid < 0)
    {
        cout << "fork error" << endl;
        return -1;
    }
    else if(pid == 0)
    {
        cout << "<----I am child----> " << getpid() << endl;
    }
    else
    {
        cout << "<----I am parent----> " << getpid() << endl; 
    }

    return 0;
}

進程狀態

運行、就緒、阻塞

R:運行狀態:但是並不意味着進程一定在運行中,它表明進程要麼是在運行要麼是在運行隊列裏

S:睡眠狀態;意味着進程在等待事件完成(可中斷休眠:能夠被喚醒的狀態)

D:磁盤休眠狀態;不可中斷休眠:要通過特定的條件才能被喚醒

T:停止狀態:可以通過發送SIGSTOP信號給進程來停止,這個被暫停的進程可以通過發送SIGCONT信號繼續運行。

X:死亡狀態:這個狀態只是一個返回狀態,不會再任務列表中看到這個狀態。

殭屍進程

處於殭屍狀態的進程會造成內存泄漏

殭屍進程產生的原因

  子進程先於父進程退出,因爲要保留推出原因,所以操作系統不能直接釋放所有資源,通知父進程獲取退出原因,允許操作系統釋放資源,但是父進程沒有關注這個通知導致子進程退出後無法釋放所有資源,處於僵死狀態,稱爲殭屍進程。

如何避免:進程等待

處理方式:退出父進程

孤兒進程

父進程先於子進程退出,子進程成爲孤兒進程,運行在後臺,父進程成爲1號進程
守護進程/精靈進程:特殊的孤兒進程,一個進程的父進程先於他推出後成爲孤兒進程,他會脫離各種會話和終端影響,將之前的所有聯繫取消,

環境變量

存儲系統運行環境參數的變量

查看環境變量
env set echo

設置環境變量
export

刪除環境變量
unset

常見環境變量
HOME PWD SHELL PATH

環境變量特性
全局特性(繼承)
當前shell終端下所運行的進程能夠獲取到當前終端所有的環境變量,但是獲取不到普通變量

環境變量在代碼中的獲取

通過函數: char* getenv(char* name)
main的第三個參數:  int main(int argc, char* argv[], char* env[])

環境變量的使用場景
  通常是父進程通過給子進程設置環境變量達到向子進程傳遞數據的功能。

程序地址空間

在這裏插入圖片描述
  內存地址就是內存區域的一個編號,變量就是一塊內存地址的別名。

虛擬地址

  虛擬地址空間,實際上是一個結構體, mm_struct,這個結構體給進程描述了一塊完整連續的虛擬地址空間,但是在物理內存中有可能是不連續的。

struct mm_struct
{
	ulong men_size;//內存大小
    ulong code_start;//代碼段的起始位置
    ulong code_end;//代碼段的結束位置
};

  每個進程都會有一份自己的程序地址空間。並且每個程序都需要一塊連續的地址空間。操作系統對每個運行起來的程序都會建立一塊虛擬的地址空間,給進程描述一塊虛假的內存空間,每一個變量都有一個虛擬的地址,但是這個虛擬的地址無法存儲數據,還是要存儲到物理內存中,這時就有一個頁表,它主要時做映射,將變量通過映射,映射到物理內存中,這塊物理內存存放的就是變量的值,子進程複製了父進程而創建,也就是說子進程和父進程有着相同的虛擬地址空間和頁表,在他的初始化全局這段空間中也有着和父進程相同的變量,也通過頁表映射一塊物理內存空間,這塊物理內存空在開始的時候是相同的,但是當子進程要修改這塊內存中的數據的時候,爲了保證進程之間的獨立性,操作系統會通過寫時拷貝技術爲子進程重新開闢一段塊物理內存空間,所以說地址相同說的是他們的虛擬地址相同。

  我們所看到的程序地址空間實際上是一個虛擬地址空間,實際上是通過操作系統通過mm_struct這個結構體爲進程描述的一個空間,因此有時候也稱作內存描述符。

爲什麼要有虛擬地址空間?

  進程通過訪問虛擬地址進而獲取變量數據,最終還是要去訪問物理內存,因爲數據是存儲在物理內存中的,在虛擬地址和物理地址之間通過頁表進行地址映射,將虛擬地址轉換得到物理地址,進而訪問物理內存區域。通過映射之後物理地址可就不一定是連續的了,通過這種映射轉換的方式實現數據的離散存儲提高了內存的利用率。頁表中不但記錄了虛擬地址和物理地址的映射關係,還記錄了這塊地址的屬性實現內存的訪問控制。

爲什麼進程要獨立

  進程應該具有獨立性,因爲獨立才更加穩定

虛擬地址空間和頁表有什麼用?

​ 提高內存利用率,增加內存訪問控制,保持進程獨立性

頁表是如何將虛擬地址轉換到物理地址

一共有三種常見的內存管理方式:分段式、分頁式和段頁式

分段式內存管理

​ 內存地址構成:段號+段內偏移
​ 段表:有很多的段項

​ (物理段的起始地址)


  分段式物理地址獲取方法:通過地址中的段號去段表中找到段表項,通過段表項中的物理起始地址加上地址中的段內偏移獲取到物理地址

​ 分頁式內存管理:

​ 內存地址的構成:頁號 + 頁內偏移

​ 頁表中有很多頁表項

在這裏插入圖片描述
  分頁式物理地址獲取方法:通過地址中的頁號去頁表中找到頁表項,通過頁表項中的物理頁號加上頁內偏移獲取到物理地址

段頁式內存管理

  內存地址:段號+段內頁號+頁內偏移

  段表向中包含段內頁表起始地址

  段內頁表中包含物理頁號

段頁式物理地址獲取方法:通過段號在段表中找到段表項,通過段表項中的段內頁表地址找到段內頁表,通過地址中的段內頁號在段內也表中找到頁表項,通過頁表項中的物理頁號與頁內偏移組成物理地址。

內存置換算法:

​ 內存中只有4G,但是想處理5G的數據怎麼辦?

​ swap分區也叫交換內存:內存不夠時,將內存中的數據置換到交換分區中,騰出內存處理數據

置換算法

FIFO:先進先出,核心原則就是:如果一個數據最先進入緩存區,則因該最早淘汰掉
LFU:最近最少頻率未使用。基於訪問次數。
LRU:最近最久未處理,基於訪問時間。

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