Linux操作系統之進程(二):進程狀態

目錄

前言

一、補充知識點

1、并行與并發

2、時間片

3、 等待的本質

4、掛起

二. 進程的基本狀態

三、代碼演示

1、R與S

?2、T

3、Z

?四、孤兒進程

總結:


前言

在操作系統中,進程是程序執行的基本單位。每個進程都有自己的狀態,這些狀態反映了進程在系統中的當前活動情況。理解進程狀態對于系統編程、性能調優和問題排查至關重要。今天,我們將深入探討Linux進程的各種狀態,并結合實際例子分析它們的行為。

一、補充知識點

在上文中我們介紹了進程的屬性與進程的創建(詳細查看上文鏈接),在了解我們本篇文章的主題——進程狀態之前,我們需要給大家先補充幾個知識點概念:

1、并行與并發

單核CPU執行進程代碼不是把進程代碼執行完畢才開始執行下一個,而是給每一個進程預分配一個時間片,基于時間片進行調度輪轉(單CPU下),這通常被稱為并發。

所謂并發,就是多個進程在一個CPU下采用的進程切換的方式,在一段時間之內讓多個進程都得以推進。

那么所謂并行呢?就是多個進程在多個CPU下分別同時進行運行,這稱之為并行。

CPU切換和運行的速度非常快,站在我們對應的這個CPU看來呢,當前這一個物理CPU它切換了多個進程,但是因為它切換和運行的速度非常快,所以用戶根本就感知不到,

所以這也就解釋為什么我們平時的死循環把不會程序卡死的原因,CPU會直接在操作系統的指導下,他會按照時間片來進行我們對應的輪轉和調度。

2、時間片

Linux /Windows民用級別的操作系統為分時操作系統

就是我們整個操作系統它在幫你去執行任務時,是會給每一個任務給他分配上對應的一個時間片的,比如說是10毫秒或者是1毫秒。把時間片分好之后,每一個進程在CPU上去運行時,他把自己的時間片耗盡了,他就必須得從CPU上去剝離下來,剝離下來之后再把另一個任務再放上去。這就叫做分時操作系統。沒有優先級的誰高誰低,特點:調度任務追求公平,這保證了保證用戶操作的及時響應。

實時操作系統通常用于VxWorks、FreeRTOS、QNX(工業/嵌入式領域),主要特點是確定性調度,任務優先級嚴格分級,高優先級任務可搶占低優先級任務,支持硬實時(Hard Real-Time)和軟實時(Soft Real-Time)

  • 硬實時:必須在絕對截止時間內完成(如航天控制系統)

  • 軟實時:允許偶爾超時(如視頻流處理)

3、 等待的本質

?等待的本質:當進程需要訪問外部設備(如鍵盤、磁盤、網絡)時,若設備未就緒,CPU 不會持續輪詢等待,而是將該進程移出運行隊列,掛入設備的等待隊列

操作系統是怎么管理硬件的呢?也是:先描述,再組織

每個硬件設備(如鍵盤、磁盤)在內核中對應一個?struct device?結構體,包含:

struct device 
{unsigned int id;          // 設備唯一標識enum device_status status;// 設備狀態(就緒/忙碌/錯誤)struct list_head wait_queue; // 等待該設備的進程隊列// 其他驅動相關字段...
};

當進程需等待設備時,其 PCB 會被鏈入設備的?wait_queue(等待隊列),直至設備觸發中斷通知就緒。

?阻塞和運行本質上都是在等待。,只是一個在硬件的等待隊列中等待,一個在CPU的運行隊列中等待

當從鍵盤中輸入數據后,操作系統會得到信息,隨后又將該PCB連入運行隊列。

本質上就是把一個PCB一會放在運行隊列里,一會放在設備等待隊列里,來回的去調用。

4、掛起

內存不足時,操作系統將非活躍進程的代碼和數據換出到磁盤(Swap 分區),僅保留 PCB 在內存,這就叫做掛起。

特點是用時間換空間:換入/換出操作增加延遲,但緩解內存壓力。

在掛起后,進程變為進程變為阻塞掛起狀態,掛起通常是在Swap分區里進行的。在云服務器中通常會禁掉Swap分區,這是為了防止頻繁的進行換入與換出操作導致性能驟降。

二. 進程的基本狀態

在Struct device中有一個status屬性,這個通常記錄了當前進程的狀態。

我們經常在一些教科書上看見以下圖片:

這樣的圖片肯定是無法讓大家清楚的明白什么是進程的狀態。?

在Linux中,進程的狀態通常可以通過?ps?或?top?命令查看,常見的有:

  • R(Running)?并不意味著進程?定在運?中,它表明進程要么是在運?中要么在運?

    隊列?。
  • S(Sleeping):可中斷睡眠(意味著進程在等待事件完成,如I/O)。

  • D(Uninterruptible Sleep):不可中斷睡眠(通常涉及硬件操作)。

  • T(Stopped):進程被暫停,可以通過發送 SIGSTOP 信號(如?kill -19給進程來停止(T)進程。這個被暫停的進程可以通過發送 SIGCONT 信號讓進程繼續運?。

  • Z(Zombie):僵尸進程(已終止但未被父進程回收)。

  • X(Dead):進程已完全終止(這個狀態只是?個返回狀態,你不會在任務列表?看到這個狀態。)。

此外,還有一些特殊狀態(以下并非全部):

  • t(Tracing stop):進程被調試器暫停(如?gdb?斷點)。

  • <(高優先級)?和?N(低優先級):調度優先級相關。

我們通常可以在終端中輸入?ps ajx或ps aux來查看:

? a:顯示?個終端所有的進程,包括其他??的進程。
? x:顯示沒有控制終端的進程,例如后臺運?的守護進程。
? j:顯示進程歸屬的進程組ID、會話ID、父進程ID,以及與作業控制相關的信息
? u:以用戶為中心的格式顯示進程信息,提供進程的詳細信息,如用戶、CPU和內存使用情況等

三、代碼演示

我們用以下代碼與指令操作給大家演示一下進程各種狀態的查看:

1、R與S

在當前路徑中我們有以下文件:

code.cpp:

#include<stdio.h>
#include<unistd.h>int main()
{int cnt=0;while(1){printf("hello world, cnt: %d,my pid : %d\n",cnt++ ,getpid());}return 0;
}

Makefile:

# 定義編譯器和編譯選項
CXX = g++
CXXFLAGS = -Wall -std=c++11# 定義目標文件和可執行文件名
TARGET = code
SRC = code.cpp# 默認目標
all: $(TARGET)# 直接生成可執行文件(不生成.o文件)
$(TARGET): $(SRC)$(CXX) $(CXXFLAGS) -o $@ $<# 清理生成的文件
clean:rm -f $(TARGET)# 運行程序
run: $(TARGET)./$(TARGET).PHONY: all clean run

首先我們調用make生成可執行文件code(exe),隨后輸入

./code

運行code可執行文件:

隨后在另外一個終端中輸入

watch -n 1 '(ps ajx | head -n 1; ps ajx | grep -w "./code" | grep -v grep)'

?查看進程狀態:

?這里有兩個code進程,第一個進程是bash創建的進程組,用于管理我們在前臺運行的code程序,我們不用管它,通過pid 3825121我們可以知道第二個code就是我們運行的程序,可以看見,code的進程狀態(就是STAT這一欄),一直在R與S中變換(+號表示?前臺進程組,受終端控制,如Ctrl+C能終止它),這是為什么呢?

進程狀態切換是由?CPU時間片調度?和?I/O等待?共同作用的結果:

  • R+(Running):進程正在CPU上執行,或位于運行隊列等待調度。

  • S+(Sleeping):進程因等待I/O(如printf到終端)被移出運行隊列。

我們的code.cpp中有著printf這個函數,這會涉及到IO的相關操作,printf不是直接輸出到屏幕,而是寫入?標準輸出緩沖區,最終通過?終端設備(如/dev/pts/0)顯示。又因為終端I/O速度遠慢于CPU,因此每次printf都可能觸發進程阻塞(進入S狀態)。

R → S:當進程調用阻塞式I/O(如printfscanf)。

S → R:當I/O操作完成(如終端準備好接收輸出)。

?2、T

依舊是原來幾個文件,我們繼續運行code:

我們在創建一個終端,輸入kill -19 +【對應進程PID】

?此時我們透過之前的查看進程狀態的終端可以發現,code進程已經變為了T狀態:

?若我們此時在輸入kill -18:

?又會發現:

程序又開始跑起來了,與之前不同的是,沒有了+號,這是因為被中斷后又繼續后,進程默認變為了后臺進程,此時在進程運行的終端上,輸入strl c是不會終止進程的,這個時候就只能通過kill -9來殺死進程:

3、Z

?在講僵尸狀態之前,我們先想一下,一個進程為什么會被創建出來呢?

一個進程會被創建出來是為了完成用戶的某個任務,那么操作系統怎么知道這個任務是否完成成功了呢?

我們以前寫代碼,比如做題,可以通過打印信息來了解,如果打印信息無關呢?

我們更改code.cpp代碼如下:

#include<stdio.h>
#include<unistd.h>// int main()
// {
//     int cnt=0;
//     while(1)
//     {
//         printf("hello world, cnt: %d,my pid : %d\n",cnt++ ,getpid());
//     }
//     return 0;
// }int main()
{int cnt=0;for(int i=0;i<10;i++){cnt++;}return 0;
}

code不再是一個無限循環代碼,重新輸入make生成可執行code文件并運行:

?可以看見,如果沒有打印信息,我們是無法知道任務完成成功了嗎?

請大家在終端上輸入:echo $?

?我們再把code.cpp中main函數的返回值設定為11呢?

return 11;

再運行:

我們發現,這次打印出的數又變成11了。細心的同學可能就有所猜測了。

沒錯,我們每個程序的main函數最后都會有一個return 返回值,當我們重新正常運行結束后,會執行return語句,這個返回的數,就會被父進程接受,告訴父進程,該進程執行任務是否成功。?

我們規定,返回0為執行任務成功,返回非0為失敗。

進程退出時:

1、代碼不會執行了,首先可以立即釋放的就是進程對應的程序信息數據

2、進程退出要有退出信息,保存在自己的task_struct內部

3、管理該進程的task_struct必須被OS維護起來,方便用戶未來進行獲取進程退出的信息

那么這個跟Z僵尸狀態有什么關聯呢?大家不要著急,我們把code.cpp更改如下:

#include<stdio.h>
#include<unistd.h>// int main()
// {
//     int cnt=0;
//     while(1)
//     {
//         printf("hello world, cnt: %d,my pid : %d\n",cnt++ ,getpid());
//     }
//     return 0;
// }// int main()
// {
//     int cnt=0;
//     for(int i=0;i<10;i++)
//     {
//         cnt++;
//     }
//     return 11;
// }int main()
{pid_t id =fork();if(id==0){//子進程int n=10;while(n--){printf("i am child, pid: %d, ppid: %d\n",getpid(),getppid());sleep(1);}}else {//父進程while(1){   printf("i am parent, pid:%d\n",getpid());sleep(1);}}return 0;
}

?運行并輸入

watch -n 1 '(ps ajx | head -n 1; ps ajx | grep -w "code" | grep -v grep)'

查看狀態:

我們可以看見,在子進程未執行完畢時, 二者都是出現S+或者R+的狀態,但是當我們子進程執行完畢后,可是父進程沒執行完畢,就會出現僵尸狀態:

僵尸狀態的進程:如果沒有人管我,我就會一直僵尸,task_struct會一直消耗內存→造成內存泄漏。

后面我們會講到:一般需要父進程讀取子進程信息,子進程才會自動退出。(調用waitpid),我們這里的代碼沒有調用waitpid,而是一直在循環,就不會去讀取子進程的退出信息,導致子進程一直處于僵尸狀態。

語言層面的內存泄漏的問題,如果在常駐的進程中出現,影響比較大:比如殺毒軟件。

?四、孤兒進程

剛剛的僵尸進程是子進程退出了,父進程還在。但如果是父進程死掉了,子進程還在呢?

更改code代碼如下:
?

#include<stdio.h>
#include<unistd.h>int main()
{pid_t id =fork();if(id==0){//子進程while(1){printf("i am child, pid: %d, ppid: %d\n",getpid(),getppid());sleep(1);}}else {//父進程while(1){   printf("i am parent, pid:%d\n",getpid());sleep(1);}}return 0;
}

我們在第三個終端(用來輸入其他指令kill時所使用的終端),輸入kill指令殺死父進程:

我們可以發現:

此時的子進程3868637的父進程已經變為1了。

那么這個1進程是什么呢?

輸入指令:

ps -fp 1

這個失去原本父進程的子進程,就被稱為孤兒進程。如果父進程先退出了,子進程還在:子進程成為孤兒進程,會被系統領養(一般是systemd,我這臺云服務器是屬于例外)?。

總結:

本文著重介紹了進程的幾個狀態,并通過各種代碼事例帶大家見識了一下狀態,并為各位介紹了什么是孤兒進程。這就是本篇博客進程狀態的主要內容,希望對各位有所幫助。有疑問可以在評論區提出!!!

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/80789.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/80789.shtml
英文地址,請注明出處:http://en.pswp.cn/web/80789.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

大數據技術全景解析:HDFS、HBase、MapReduce 與 Chukwa

大數據技術全景解析&#xff1a;HDFS、HBase、MapReduce 與 Chukwa 在當今這個信息爆炸的時代&#xff0c;大數據已經成為企業競爭力的重要組成部分。從電商的用戶行為分析到金融的風險控制&#xff0c;從醫療健康的數據挖掘到智能制造的實時監控&#xff0c;大數據技術無處不…

學習 Android(十一)Service

簡介 在 Android 中&#xff0c;Service 是一種無界面的組件&#xff0c;用于在后臺執行長期運行或跨進程的任務&#xff0c;如播放音樂、網絡下載或與遠程服務通信 。Service 可分為“啟動型&#xff08;Started&#xff09;”和“綁定型&#xff08;Bound&#xff09;”兩大…

投標環節:如何科學、合理地介紹 Elasticsearch 國產化替代方案——Easysearch?

一、Easysearch 定義 Easysearch 是由極限科技&#xff08;INFINI Labs&#xff09;自主研發的分布式搜索型數據庫&#xff0c;作為 Elasticsearch 的國產化替代方案&#xff0c;基于 Elasticsearch 7.10.2 開源版本深度優化[1]。 插一句&#xff1a;Elasticsearch 7.10.2 是里…

NVC++ 介紹與使用指南

文章目錄 NVC 介紹與使用指南NVC 簡介安裝 NVC基本使用編譯純 C 程序編譯 CUDA C 程序 關鍵編譯選項示例代碼使用標準并行算法 (STDPAR)混合 CUDA 和 C 優勢與限制優勢限制 調試與優化 NVC 介紹與使用指南 NVC 是 NVIDIA 提供的基于 LLVM 的 C 編譯器&#xff0c;專為 GPU 加速…

Veo 3 可以生成視頻,并附帶配樂

谷歌最新的視頻生成 AI 模型 Veo 3 可以創建與其生成的剪輯相配的音頻。 周二&#xff0c;在谷歌 I/O 2025 開發者大會上&#xff0c;谷歌發布了 Veo 3。該公司聲稱&#xff0c;這款產品可以生成音效、背景噪音&#xff0c;甚至對話&#xff0c;為其制作的視頻增添配樂。谷歌表…

Android本地語音識別引擎深度對比與集成指南:Vosk vs SherpaOnnx

技術選型對比矩陣 對比維度VoskSherpaOnnx核心架構基于Kaldi二次開發ONNX Runtime + K2新一代架構模型格式專用格式(需專用工具轉換)ONNX標準格式(跨框架通用)中文識別精度89.2% (TDNN模型)92.7% (Zipformer流式模型)內存占用60-150MB30-80MB遲表現320-500ms180-300ms多線程…

十四、Hive 視圖 Lateral View

作者&#xff1a;IvanCodes 日期&#xff1a;2025年5月20日 專欄&#xff1a;Hive教程 在Hive中&#xff0c;我們經常需要以不同于原始表結構的方式查看或處理數據。為了簡化復雜查詢、提供數據抽象&#xff0c;以及處理復雜數據類型&#xff08;如數組或Map&#xff09;&#…

微軟開源GraphRAG的使用教程-使用自定義數據測試GraphRAG

微軟在今年4月份的時候提出了GraphRAG的概念,然后在上周開源了GraphRAG,Github鏈接見https://github.com/microsoft/graphrag,截止當前,已有6900+Star。 安裝教程 官方推薦使用Python3.10-3.12版本,我使用Python3.10版本安裝時,在初始化項目過程中會報錯,切換到Python3.…

XXX企業云桌面系統建設技術方案書——基于超融合架構的安全高效云辦公平臺設計與實施

目錄 1. 項目背景與目標1.1 背景分析1.2 建設目標2. 需求分析2.1 功能需求用戶規模與場景終端兼容性2.2 非功能需求3. 系統架構設計3.1 總體架構圖流程圖說明3.2 技術選型對比3.3 網絡設計帶寬規劃公式4. 詳細實施方案4.1 分階段部署計劃4.2 桌面模板配置4.3 測試方案性能測試工…

數據直觀分析與可視化

數據直觀分析與可視化 一、數據的直觀分析核心價值 數據的直觀分析旨在通過視覺化的方式&#xff0c;幫助人們更直觀、更快速地理解數據的特征和模式&#xff0c;從而發現趨勢、異常值、分布情況以及變量之間的關系&#xff0c;為決策提供支持。 數據可視化與信息圖形、信息可…

Neo4j數據庫

Neo4j 是一款專門用來處理復雜關系的數據庫。我們可以簡單地將它理解為一個“用圖結構來管理數據的工具”。與我們常見的&#xff0c;像 Excel 那樣用表格&#xff08;行和列&#xff09;來存儲數據的傳統數據庫不同&#xff0c;Neo4j 采用了一種更接近人類思維對現實世界理解的…

Java異常處理全解析:從基礎到自定義

目錄 &#x1f680;前言&#x1f914;異常的定義與分類&#x1f4af;運行時異常&#x1f4af;編譯時異常&#x1f4af;異常的基本處理 &#x1f31f;異常的作用&#x1f427;自定義異常&#x1f4af;自定義運行時異常&#x1f4af;自定義編譯時異常 ??異常的處理方案&#x1…

Redisson分布式集合原理及應用

Redisson是一個用于Redis的Java客戶端&#xff0c;它簡化了復雜的數據結構和分布式服務的使用。 適用場景對比 數據結構適用場景優點RList消息隊列、任務隊列、歷史記錄分布式共享、阻塞操作、分頁查詢RMap緩存、配置中心、鍵值關聯數據支持鍵值對、分布式事務、TTLRSet去重集…

打破次元壁,VR 氣象站開啟氣象學習新姿勢?

在教育領域&#xff0c;VR 氣象站同樣發揮著巨大的作用&#xff0c;為氣象教學帶來了全新的模式&#xff0c;打破了傳統教學的次元壁&#xff0c;讓學生們以全新的姿勢學習氣象知識。? 在傳統的氣象教學中&#xff0c;學生們主要通過課本、圖片和老師的講解來學習氣象知識。這…

k8s面試題-ingress

場景&#xff1a;我通過deployment更新pod&#xff0c;ingress是怎么把新的請求流量發送到我新的pod的&#xff1f;是怎么監控到我更新的pod的&#xff1f; 在 Kubernetes 中&#xff0c;Ingress 是一種 API 對象&#xff0c;用于管理外部訪問到集群內服務的 HTTP 和 HTTPS 路…

RHCE 練習三:架設一臺 NFS 服務器

一、題目要求 1、開放 /nfs/shared 目錄&#xff0c;供所有用戶查詢資料 2、開放 /nfs/upload 目錄&#xff0c;為 192.168.xxx.0/24 網段主機可以上傳目錄&#xff0c;并將所有用戶及所屬的組映射為 nfs-upload,其 UID 和 GID 均為 210 3.將 /home/tom 目錄僅共享給 192.16…

【動態導通電阻】GaN HEMT動態導通電阻的精確測量

2023 年 7 月,瑞士洛桑聯邦理工學院的 Hongkeng Zhu 和 Elison Matioli 在《IEEE Transactions on Power Electronics》期刊發表了題為《Accurate Measurement of Dynamic ON-Resistance in GaN Transistors at Steady-State》的文章,基于提出的穩態測量方法,研究了氮化鎵(…

AI 制作游戲美術素材流程分享(程序員方向粗糙版)

AI 制作游戲美術素材分享(程序員方向粗糙版) 視頻講解: 抖音:https://www.douyin.com/user/self?from_tab_namemain&modal_id7505691614690561295&showTabpost Bilibili: https://www.bilibili.com/video/BV1ojJGzZEve/ 寫在最前面: 本方法比較粗糙,只對對美術風…

Java求職面試:互聯網大廠技術棧深度解析

文章簡述 在這篇文章中&#xff0c;我們將通過一個模擬的面試場景&#xff0c;帶你深入了解Java求職面試中可能會遇到的技術棧問題。通過這個故事&#xff0c;你可以學習到相關技術點的具體應用場景和面試技巧。 正文 場景&#xff1a;某互聯網大廠的面試現場 面試官&#…

學習日記-day11-5.20

完成目標&#xff1a; comment.java package com.zcr.pojo; import org.hibernate.annotations.GenericGenerator;import javax.persistence.*; //JPA操作表中數據&#xff0c;可以將對應的實體類映射到一張表上Entity(name "t_comment")//表示當前的實體類與哪張表…