Linux中的進程

進程控制

fork 函數

fork 函數從已存在的進程中創建新的進程,已存在進程為父進程,新創建進程為子進程

fork 的常規用法

  1. 一個父進程希望復制自己,使父子進程同時執行不同的代碼段。例如,父進程等待客戶端請求,生成子進程來處理請求
  2. 一個進程要執行一個不同的程序。例如子進程從fork返回后,調用exec函數

fork 失敗的原因

  1. 系統中有太多的進程
  2. 實際用戶的進程數超過了限制

fork 通過寫時拷貝的方式進行內容的修改:

通常,父子代碼共享,父子再不寫入時,數據也是共享的,當任意一方試圖寫入,便以寫時拷貝的方式各自一份副本

返回值:子進程返回 0,父進程返回子進程 pid,出錯返回-1

返回子進程 pid 的原因:方便管理子進程

進程創建時,先分配 task_struct 然后分配空間

進程退出時,先回收資源,然后銷毀 task_struct

僵尸進程

一個進程的關閉是先回收資源,然后再將 PCB 清理,僵尸進程就是 PCB 未被清理的進程

在系統中有個 "?" 環境變量,這個變量用來獲取子進程的返回值,0 表示成功,非零表示失敗,同時不同的非零值可以表示不同的失敗原因,雖然status是int,但是僅有低8位可以被父進程所用。所以_exit(-1)時,在終端執行$?發現返回值是255

進程退出的方式

  • 正常終止(可以通過echo $? 查看進程退出碼):
  1. 從main返回
  2. 調用exit
  3. _exit
  • 異常退出:
  1. ctrl + c,信號終止

進程退出時,會產生退出碼和退出信號,進程會將這兩個值寫入 PCB 中,這樣就獲取到了退出的信息,如果進程是異常的,將會產生退出信號,通過退出信號就能判斷出異常的原因,如果沒有退出信息,就可以繼續查看退出碼

_exit 函數

#include<unistd.h>
void _exit(int status); 
//參數:status 定義了進程的終止狀態,父進程通過wait來獲取該值 
//雖然status是int,但是僅有低8位可以被父進程所用。
//所以_exit(-1)時,在終端執行$?發現返回值是255。

exit 函數

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

exit 和 _exit 的區別

  • exit()正常終止,會執行清理操作。
  • _exit()立即終止跳過清理操作,更底層。
  • _exit() 是一個低層次系統調用,直接返回內核。
    • 它保證:
    • 立刻終止
    • 不執行任何用戶態清理邏輯

子進程退出時推薦使用 _exit,因為 _exit 不會刷新緩沖區,避免了多次刷新,因為在父進程結束時還會再刷新一次

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>int main() {printf("Hello world\n"); // 緩沖區中暫存pid_t pid = fork();if (pid == 0) {// 子進程exit(0);  // 刷新緩沖區 → 輸出 "Hello world"} else {// 父進程wait(NULL);}return 0;  // 父進程也刷新緩沖區 → 再輸出一次
}

exit() 在退出進程時會刷新緩沖區,而 _exit() 不會

exit() 是庫函數,而 _exit() 是系統調用

exit最后也會調用 _exit,
但在調用exit之前,還做了其他工作:

  1. 執行用戶通過 atexit或on_exit定義的清理函數。
  2. 關閉所有打開的流,所有的緩存數據均被寫入
  3. 調用_exit

return退出

return是一種常見的退出進程方法。執行return n;等同于執行exit(n),因為調用main的運行時函數會將main的返回值當做 exit的參數

僵死狀態(Zombies)是一個比較特殊的狀態。當進程退出并且父進程沒有讀取到子進程退出的返回代碼時就會產生僵死(尸)進程

進程等待

子進程退出后,若父進程不進行任何操作,將會產生僵尸進程,造成內存泄漏,一旦進程變成僵尸進程,將會無法被殺死

進行進程等待的原因:

父進程通過等待來解決僵尸進程的問題

父進程獲取子進程的退出信息,知道子進程是什么原因退出的

wait

#include <sys/types.h>/* 提供類型pid_t的定義*/#include <wait.h>pid_t wait(int *status)
返回值:成功返回被等待進程pid,失敗返回-1。
參數:輸出型參數,獲取子進程退出狀態,不關心則可以設置成為NULL

作用:進程一旦調用了wait,就會立刻阻塞自己,由wait分析當前進程中的某個子進程是否已經退出了,如果讓它找到這樣一個已經變成僵尸進程的子進程,wait會收集這個子進程的信息,并將它徹底銷毀后返回;如果沒有找到這樣一個子進程,wait會一直阻塞直到有一個出現

等待就是將父進程設置為 S 狀態,然后將父進程的 PCB 鏈接到子進程,此時就能獲取到子進程的退出狀態

阻塞:子進程沒有結束,父進程執行 wait,等待某種條件的發生,此時就發生了阻塞,阻塞本質上就是進程不在調度隊列上,CPU 不執行進程的代碼

非阻塞等待:在等待的過程中還可以繼續執行進程, 調用者立刻返回,如果事件未發生,不會停在那里等待 ,可能會導致沒有等待到子進程的問題

阻塞等待:在等待過程中只能等待,不能做其他任何事情,不就緒就不返回

特性

阻塞等待

非阻塞等待

調用行為

卡住,直到子進程結束

立即返回,可能沒等到子進程

CPU 資源使用

更節省(系統調度)

需要你自己輪詢,可能浪費 CPU

使用場景

同步執行、流程控制

異步程序、服務端進程池管理

接口實現

wait(), waitpid(..., 0)

waitpid(..., WNOHANG)

waitpid

 waitpid(pid_t pid, int *status, int options); 
返回值:當正常返回的時候waitpid返回收集到的子進程的進程ID;如果設置了選項WNOHANG,而調用中waitpid發現沒有已退出的子進程可收集,則返回0;如果調用中出錯,則返回-1,這時errno會被設置成相應的值以指示錯誤所在;返回值>0:等待成功,子進程退出,父進程成功獲取退出信息返回值<0:等待失敗返回值==0:檢測成功,但是子進程還沒退出,需要下一次重復等待參數:pid:Pid=-1,等待任一個子進程。與wait等效。Pid>0.等待其進程ID與pid相等的子進程。status:WIFEXITED(status): 若為正常終止子進程返回的狀態,則為真。(查看進程是否是正常退出)WEXITSTATUS(status): 若WIFEXITED非零,提取子進程退出碼。(查看進程的退出碼)options:WNOHANG: 若pid指定的子進程沒有結束,則waitpid()函數返回0,不予以等待。若正常結束,則返回該子進程的ID。(非阻塞等待)

如果子進程已經退出,調用wait/waitpid時,wait/waitpid會立即返回,并且釋放資源,獲得子進程退出信息

如果在任意時刻調用wait/waitpid,子進程存在且正常運行,則進程可能阻塞

如果不存在該子進程,則立即出錯返回

waitpid 作用和 pid 等價

等待失敗的情況:id 值填錯

非阻塞等待+循環=非阻塞輪詢,較為常用,能夠允許父進程在等待的時候進行其他操作

獲取子進程status

wait和waitpid,都有一個status參數,該參數是一個輸出型參數,由操作系統填充。

如果傳遞NULL,表示不關心子進程的退出狀態信息。 否則,操作系統會根據該參數,將子進程的退出信息反饋給父進程

status不能簡單的當作整形來看待,可以當作位圖來看待,具體細節如下圖(只研究status低16比特位):

waitpid 和 wait 的區別

函數

wait

waitpid

原型

pid_t wait(int *status);

pid_t waitpid(pid_t pid, int *status, int options);

功能

等待任意一個子進程結束

根據條件等待一個或多個特定子進程結束

waitpid 的參數:

  • pid: 可指定要等待的子進程。
    • >0: 等待指定 PID 的子進程。
    • -1: 等價于 wait,等待任意一個子進程。
    • 0: 等待與當前進程同組的任何子進程。
    • <-1: 等待特定進程組 ID 的任何子進程。
  • options: 控制行為,例如:
    • WNOHANG: 非阻塞地檢查子進程是否結束。
    • WUNTRACED: 也報告已停止(但未終止)的子進程。

獲取子進程 status

  • wait和waitpid,都有一個status參數,該參數是一個輸出型參數,由操作系統填充
  • 如果傳遞NULL,表示不關心子進程的退出狀態信息
  • 否則,操作系統會根據該參數,將子進程的退出信息反饋給父進程
  • status不能簡單的當作整形來看待,可以當作位圖來看待,具體細節如下圖(只研究status低16比特位)

printf("child exit code:%d\n", (status>>8)&0XFF);
//退出狀態在前8位,因此將后8位移除,通過和FF與操作,將退出狀態中為1的保留,為0的舍去
printf("sig code : %d\n", status&0X7F );//將低7位按位與得到終止信號

?

進程程序替換

用fork創建子進程后執行的是和父進程相同的程序(但有可能執行不同的代碼分支),子進程往往要調用一種exec函數以執行另一個程序。當進程調用一種exec函數時,該進程的用戶空間代碼和數據完全被新程序替換,從新程序的啟動例程開始執行。調用exec并不創建新進程,所以調用exec前后該進程的id并未改變

exec 函數可以讓進程替換掉自己的代碼和數據轉而執行其他的程序

原理:exec 將被替換程序從外存中加載到內存中,將原來進程的代碼和數據替換掉,task_struct 并沒有被替換,因此也就沒有創建新的進程

exec*系列函數在執行完畢后,后面的代碼也就不會被執行了,因為已經被 exec 所執行的函數替換掉了

exec*可以不用在乎其返回值,因為一旦執行成功,后續的代碼全部被替換;

一旦失敗,就會繼續向下執行

子進程執行 exec 時,由于進程具有獨立性,因此會將原來父進程的數據和代碼重新拷貝一份,然后在新拷貝的地方進行替換代碼,這樣就不會影響父進程,此時的父子進程就在數據結構和代碼層面上徹底的分離了

一共有 6 中 exec 系列函數#include<unistd.h>
int execl(const char *path, const char *arg, ...);后面可以加上多個命令的參數,但必須以NULL結尾
int execlp(const char *file, const char *arg, ...);不需要傳程序的路徑,因為會自動在環境變量中查找
int execle(const char *path, const char *arg, ...,char *const envp[]);
int execv(const char *path, char *const argv[]);和execl類似,只不過是將參數放入argv[]中
int execvp(const char *file, char *const argv[]);不需要傳程序的路徑,因為會自動在環境變量中查找
int execve(const char *path, char *const argv[], char *const envp[]);envp為環境變量,可以傳入,也可以自定義,整體把所有環境變量替換掉

命名解釋: l(list) : 表示參數采用列表

v(vector) : 參數用數組

p(path) : 有p自動搜索環境變量PATH

e(env) : 表示自己維護環境變量

exec/exit就像call/return

一個C程序有很多函數組成。一個函數可以調用另外一個函數,同時傳遞給它一些參數。被調用的函數執行一定的操作,然后返回一個值。每個函數都有他的局部變量,不同的函數通過call/return系統進行通信。

這種通過參數和返回值在擁有私有數據的函數間通信的模式是結構化程序設計的基礎。Linux鼓勵將這種應用于程序之內的模式擴展到程序之間。如下圖

一個C程序可以fork/exec另一個程序,并傳給它一些參數。這個被調用的程序執行一定的操作,然后通過exit(n)來 返回值。調用它的進程可以通過wait(&ret)來獲取exit的返回值

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

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

相關文章

EDR與XDR如何選擇適合您的網絡安全解決方案

1. 什么是EDR&#xff1f; 端點檢測與響應&#xff08;EDR&#xff09; 專注于保護端點設備&#xff08;如電腦、服務器、移動設備&#xff09;。通過在端點安裝代理軟件&#xff0c;EDR實時監控設備活動&#xff0c;檢測威脅并快速響應。 EDR核心功能 實時監控&#xff1a;…

AGI大模型(21):混合檢索之混合搜索

為了執行混合搜索,我們結合了 BM25 和密集檢索的結果。每種方法的分數均經過標準化和加權以獲得最佳總體結果 1 代碼 先編寫 BM25搜索的代碼,再編寫密集檢索的代碼,最后進行混合。 from rank_bm25 import BM25Okapi from nltk.tokenize import word_tokenize import jieb…

2025最新的軟件測試面試大全(含答案+文檔)

一、軟件測試基礎面試題 1、闡述軟件生命周期都有哪些階段? 常見的軟件生命周期模型有哪些? 軟件生命周期是指一個計算機軟件從功能確定設計&#xff0c;到開發成功投入使用&#xff0c;并在使用中不斷地修改、增補和完善&#xff0c;直到停止該軟件的使用的全過程(從醞釀到…

C++.神經網絡與深度學習(二次修改)

神經網絡與深度學習 1. 神經網絡基礎1.1 神經元模型與激活函數1.2 神經網絡結構與前向傳播2.1 損失函數與優化算法均方誤差損失函數交叉熵損失函數梯度下降優化算法2.2 反向傳播與梯度計算神經元的反向傳播3.1 神經元類設計與實現神經元類代碼實現代碼思路3.2 神經網絡類構建神…

FPGA圖像處理(六)------ 圖像腐蝕and圖像膨脹

默認迭代次數為1&#xff0c;只進行一次腐蝕、膨脹 一、圖像腐蝕 1.相關定義 2.圖像腐蝕效果圖 3.fpga實現 彩色圖像灰度化&#xff0c;灰度圖像二值化&#xff0c;圖像緩存生成濾波模塊&#xff08;3*3&#xff09;&#xff0c;圖像腐蝕算法 timescale 1ns / 1ps // // Des…

中國版Cursor:CodeBuddy騰訊云代碼助手使用體驗

我正在參加CodeBuddy「首席試玩官」內容創作大賽&#xff0c;本文所使用的 CodeBuddy 免費下載鏈接&#xff1a;騰訊云代碼助手 CodeBuddy - AI 時代的智能編程伙伴” 1.CodeBuddy簡介 騰訊云代碼助手CodeBuddy&#xff0c;這個是一款編程插件&#xff0c;我們可以在各個編程…

Go語言 GORM框架 使用指南

在 Go 語言社區中&#xff0c;數據庫交互一直是開發者們關注的重點領域&#xff0c;不同開發者基于自身的需求和偏好&#xff0c;形成了兩種主要的技術選型流派。一部分開發者鐘情于像sqlx這類簡潔的庫&#xff0c;盡管其功能并非一應俱全&#xff0c;但它賦予開發者對 SQL 語句…

從零開始學習three.js(18):一文詳解three.js中的著色器Shader

在WebGL和Three.js的3D圖形渲染中&#xff0c;著色器&#xff08;Shader&#xff09; 是實現復雜視覺效果的核心工具。通過編寫自定義的著色器代碼&#xff0c;開發者可以直接操作GPU&#xff0c;實現從基礎顏色渲染到動態光照、粒子效果等高級圖形技術。本文將深入解析Three.j…

Python函數庫調用實戰:以數據分析為例

一、引言 Python之所以在編程領域廣受歡迎&#xff0c;很大程度上得益于其豐富且強大的函數庫。這些函數庫涵蓋了從數據分析、科學計算到Web開發、機器學習等眾多領域&#xff0c;極大地提高了開發效率。本文將以數據分析為例&#xff0c;介紹如何調用Python的一些常用函數庫。…

shell腳本之條件判斷,循環控制,exit詳解

if條件語句的語法及案例 一、基本語法結構 1. 單條件判斷 if [ 條件 ]; then命令1命令2... fi2. 雙分支&#xff08;if-else&#xff09; if [ 條件 ]; then條件為真時執行的命令 else條件為假時執行的命令 fi3. 多分支&#xff08;if-elif-else&#xff09; if [ 條件1 ]…

現代 Web 自動化測試框架對比:Playwright 與 Selenium 的深度剖析

現代 Web 自動化測試框架對比&#xff1a;Playwright 與 Selenium 的深度剖析 摘要&#xff1a;本文對 Playwright 與 Selenium 在開發適配性、使用難度、場景適用性及性能表現等方面進行了全面深入的對比分析。通過詳細的技術實現細節闡述與實測數據支撐&#xff0c;為開發者…

系統架構設計(十):結構化編程

定義 結構化編程是一種遵循清晰邏輯結構、避免使用 goto 的編程方法。它強調使用有限的三種基本控制結構來組織程序&#xff0c;提高程序的可讀性、可維護性和可測試性。 它是現代程序設計的基礎&#xff0c;被廣泛應用于命令式語言&#xff08;如 C、Pascal、Java&#xff0…

TC3xx學習筆記-UCB BMHD使用詳解(二)

文章目錄 前言Confirmation的定義Dual UCB: Confirmation StatesDual UCB: Errored State or ECC Error in the UCB Confirmation CodesECC Error in the UCB ContentDual Password UCB ORIG and COPY Re-programming UCB_BMHDx_ORIG and UCB_BMHDx_COPY (x 0-3)BMHD Protecti…

OTA與boot loader

OTA指的是無線升級&#xff0c;通常用于更新設備的固件或軟件&#xff0c;用戶不用手動操作&#xff0c;非常方便。而bootloader是啟動時加載操作系統的程序&#xff0c;負責硬件初始化和啟動流程。 首先&#xff0c;OTA是如何通過bootloader工作的。OTA下載更新包后&#xff0…

實驗六:FPGA序列檢測器實驗

FPGA序列檢測器實驗(遠程實驗系統) 文章目錄 FPGA序列檢測器實驗(遠程實驗系統)一、數字電路基礎知識1. 時鐘與同步2. 按鍵消抖原理代碼講解:分頻與消抖3. 有限狀態機(FSM)設計代碼講解:狀態機編碼與轉移4. 邊沿檢測與信號同步5. 模塊化設計二、實驗數字電路整體思想三…

jenkins部署

開發者將代碼push到git運維人員通過jenkins部署&#xff0c;自動到git上pull代碼通過maven構建成jar包&#xff0c;并結合dockerfile打包成鏡像&#xff0c;push docker鏡像到docker registry通過k8s發起 發布/更新 服務 操作 通過Jenkins部署&#xff0c;自動到Git上PULL代碼 …

BBR 的 buffer 動力學觀感

這周很忙&#xff0c;今天還加了一天班&#xff0c;但還是抽空實現了五一在安徽涇縣山區喝著一壺酒寫的 BBR ProbeRTT 的想法&#xff0c;沒多少行代碼&#xff0c;它真就消除了帶寬鋸齒&#xff0c;皮了個鞋&#x1f45e;&#xff0c;昨天我還在群里說了今天再說說 BBR 的&…

第9講、深入理解Scaled Dot-Product Attention

Scaled Dot-Product Attention是Transformer架構的核心組件&#xff0c;也是現代深度學習中最重要的注意力機制之一。本文將從原理、實現和應用三個方面深入剖析這一機制。 1. 基本原理 Scaled Dot-Product Attention的本質是一種加權求和機制&#xff0c;通過計算查詢(Query…

el-tree結合checkbox實現數據回顯

組件代碼 <el-tree:data"vertiList"show-checkboxnode-key"id":props"defaultProps"ref"treeRefx"class"custom-tree"check-change"handleCheckChange"> </el-tree>獲取選擇的節點 handleCheckChan…

OpenResty 深度解析:構建高性能 Web 服務的終極方案

引言 openresty是什么&#xff1f;在我個人對它的理解來看相當于嵌入了lua的nginx; 我們在nginx中嵌入lua是為了不需要再重新編譯,我們只需要重新修改lua腳本,隨后重啟即可; 一.lua指令序列 我們分別從初始化階段&#xff0c;重寫/訪問階段&#xff0c;內容階段&#xff0c;日志…