Linux 入門八:Linux 多進程

一、概述

1.1 什么是進程?

在 Linux 系統中,進程是程序的一次動態執行過程。程序是靜態的可執行文件,而進程是程序運行時的實例,系統會為其分配內存、CPU 時間片等資源。例如,輸入 ls 命令時,系統創建進程執行 ls 程序來顯示文件列表。進程是資源分配的基本單位,理解進程對掌握 Linux 系統運行機制至關重要。

1.2 查看進程

在 Linux 中,可使用 ps 命令查看系統中當前運行的進程。下面是一些常用的 ps 命令參數組合:

  • ps -ef
    • 功能:以全格式顯示所有進程的詳細信息。
    • 步驟
      1. 打開終端。
      1. 輸入 ps -ef 并回車。
    • 示例輸出
 
UID PID PPID C STIME TTY TIME CMDroot 1 0 0 00:00 ? 00:00:01 /sbin/init splashroot 2 0 0 00:00 ? 00:00:00 [kthreadd]

  • 參數解釋
    • UID:進程所有者的用戶 ID。
    • PID:進程的 ID 號。
    • PPID:父進程的 ID 號。
    • C:CPU 占用率。
    • STIME:進程啟動時間。
    • TTY:進程關聯的終端。
    • TIME:進程使用的 CPU 時間。
    • CMD:啟動進程的命令。

在 Linux 中,除了 ps -ef,還可使用以下命令查看進程:?

  • ps aux:?
  • 功能:顯示所有進程的詳細資源使用情況(如內存、CPU 占用率)。?
  • 示例輸出:?
TypeScript取消自動換行復制USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND ?root 1 0.0 0.1 24176 4360 ? Ss 00:00 0:01 /sbin/init splash ?

  • %CPU:CPU 占用百分比。?
  • %MEM:內存占用百分比。?
  • STAT:進程狀態(如 S 表示睡眠,R 表示運行)。?
  • top 命令:?
  • 功能:動態實時顯示進程資源占用情況,類似 Windows 任務管理器。?
  • 操作:輸入 top 后,可按 q 退出。

二、進程的創建

2.1 fork 函數

在 C 語言里,fork 函數是創建新進程的關鍵函數。它的作用是復制當前進程,生成一個子進程,原進程則成為父進程。fork 函數的原型如下:

#include <unistd.h>pid_t fork(void);
  • 返回值
    • 在父進程中,fork 函數返回子進程的 PID(一個正整數)。
    • 在子進程中,fork 函數返回 0。
    • 若 fork 失敗,返回 -1。
創建進程的步驟
  1. 包含必要的頭文件:#include <unistd.h> 和 #include <stdio.h>。
  1. 調用 fork 函數創建子進程。
  1. 根據 fork 的返回值判斷當前是父進程還是子進程,并執行相應的代碼。
示例代碼
#include <unistd.h>#include <stdio.h>int main() {pid_t pid;pid = fork();if (pid < 0) {perror("fork 失敗");} else if (pid == 0) {// 子進程printf("我是子進程,我的 PID 是 %d,父進程的 PID 是 %d\n", getpid(), getppid());} else {// 父進程printf("我是父進程,我的 PID 是 %d,子進程的 PID 是 %d\n", getpid(), pid);}return 0;}
編譯和運行步驟
  1. 把上述代碼保存為 fork_example.c。
  1. 打開終端,進入代碼所在目錄。
  1. 使用 gcc 編譯代碼:gcc fork_example.c -o fork_example。
  1. 運行編譯后的可執行文件:./fork_example。

三、僵尸進程

3.1 形成條件

僵尸進程的形成需要滿足以下三個條件:

  1. 子進程優先于父進程結束。
  1. 父進程不結束。
  1. 父進程不調用 wait 函數。

當子進程結束時,它會向父進程發送一個 SIGCHLD 信號,但如果父進程沒有調用 wait 或 waitpid 函數來回收子進程的資源,子進程就會變成僵尸進程。

3.2 如何避免僵尸進程

方法一:父進程調用 wait 函數

wait 函數的作用是等待任意一個子進程結束,并回收其資源。其原型如下:

#include <sys/types.h>#include <sys/wait.h>pid_t wait(int *status);
  • 參數:status 用于存儲子進程的退出狀態。
  • 返回值:返回結束的子進程的 PID。
示例代碼
#include <unistd.h>#include <stdio.h>#include <sys/types.h>#include <sys/wait.h>int main() {pid_t pid;pid = fork();if (pid < 0) {perror("fork 失敗");} else if (pid == 0) {// 子進程printf("子進程開始執行,PID 是 %d\n", getpid());sleep(2);printf("子進程結束\n");} else {// 父進程int status;pid_t child_pid = wait(&status);printf("父進程回收了 PID 為 %d 的子進程\n", child_pid);}return 0;}
方法二:使用 signal 函數處理 SIGCHLD 信號

可通過 signal 函數捕獲 SIGCHLD 信號,并在信號處理函數中調用 wait 或 waitpid 函數。

示例代碼
#include <unistd.h>#include <stdio.h>#include <sys/types.h>#include <sys/wait.h>#include <signal.h>void sigchld_handler(int signo) {pid_t pid;int status;while ((pid = waitpid(-1, &status, WNOHANG)) > 0) {printf("回收了 PID 為 %d 的子進程\n", pid);}}int main() {signal(SIGCHLD, sigchld_handler);pid_t pid;pid = fork();if (pid < 0) {perror("fork 失敗");} else if (pid == 0) {// 子進程printf("子進程開始執行,PID 是 %d\n", getpid());sleep(2);printf("子進程結束\n");} else {// 父進程printf("父進程繼續執行\n");sleep(5);}return 0;}

四、孤兒進程

4.1 形成條件

孤兒進程的形成需要滿足以下兩個條件:

  1. 父進程優先于子進程結束。
  1. 子進程未結束。

當父進程結束后,子進程就會變成孤兒進程,此時它會被進程 ID 為 1 的 init 進程接管。

4.2 被進程 ID 為 1 的進程接管

init 進程會負責回收孤兒進程的資源,確保系統資源不會被浪費。

示例代碼
#include <unistd.h>#include <stdio.h>int main() {pid_t pid;pid = fork();if (pid < 0) {perror("fork 失敗");} else if (pid == 0) {// 子進程printf("子進程開始執行,父進程 PID 是 %d\n", getppid());sleep(5);printf("子進程繼續執行,父進程 PID 是 %d\n", getppid());} else {// 父進程printf("父進程結束\n");}return 0;}

在這個示例中,父進程會先結束,子進程在睡眠 5 秒后,會發現自己的父進程 ID 變成了 1。

五、守護進程(后臺進程)

5.1 實現過程

守護進程是一種在后臺持續運行的進程,通常在系統啟動時就開始運行,并且不受用戶登錄和注銷的影響。以下是創建守護進程的詳細步驟:

步驟 1:創建子進程,父進程退出
#include <unistd.h>#include <stdio.h>#include <stdlib.h>int main() {pid_t pid;pid = fork();if (pid < 0) {perror("fork 失敗");exit(EXIT_FAILURE);}if (pid > 0) {// 父進程退出exit(EXIT_SUCCESS);}// 子進程繼續執行// 后續步驟...return 0;}
步驟 2:在子進程中創建新會話
#include <unistd.h>#include <stdio.h>#include <stdlib.h>#include <sys/types.h>#include <sys/stat.h>int main() {pid_t pid;pid = fork();if (pid < 0) {perror("fork 失敗");exit(EXIT_FAILURE);}if (pid > 0) {// 父進程退出exit(EXIT_SUCCESS);}// 子進程創建新會話pid_t sid = setsid();if (sid < 0) {perror("setsid 失敗");exit(EXIT_FAILURE);}// 后續步驟...return 0;}

setsid 函數的作用是創建一個新的會話,使子進程成為新會話的首進程,并且脫離原有的控制終端。

步驟 3:改變工作目錄
#include <unistd.h>#include <stdio.h>#include <stdlib.h>#include <sys/types.h>#include <sys/stat.h>int main() {pid_t pid;pid = fork();if (pid < 0) {perror("fork 失敗");exit(EXIT_FAILURE);}if (pid > 0) {// 父進程退出exit(EXIT_SUCCESS);}// 子進程創建新會話pid_t sid = setsid();if (sid < 0) {perror("setsid 失敗");exit(EXIT_FAILURE);}// 改變工作目錄if (chdir("/") < 0) {perror("chdir 失敗");exit(EXIT_FAILURE);}// 后續步驟...return 0;}

chdir 函數用于將工作目錄切換到根目錄,避免工作目錄被卸載導致進程無法正常工作。

步驟 4:設置文件權限掩碼
#include <unistd.h>#include <stdio.h>#include <stdlib.h>#include <sys/types.h>#include <sys/stat.h>int main() {pid_t pid;pid = fork();if (pid < 0) {perror("fork 失敗");exit(EXIT_FAILURE);}if (pid > 0) {// 父進程退出exit(EXIT_SUCCESS);}// 子進程創建新會話pid_t sid = setsid();if (sid < 0) {perror("setsid 失敗");exit(EXIT_FAILURE);}// 改變工作目錄if (chdir("/") < 0) {perror("chdir 失敗");exit(EXIT_FAILURE);}// 設置文件權限掩碼umask(0);// 后續步驟...return 0;}

umask 函數用于設置文件權限掩碼,確保守護進程創建的文件具有預期的權限。

步驟 5:關閉不需要的文件描述符
#include <unistd.h>#include <stdio.h>#include <stdlib.h>#include <sys/types.h>#include <sys/stat.h>int main() {pid_t pid;pid = fork();if (pid < 0) {perror("fork 失敗");exit(EXIT_FAILURE);}if (pid > 0) {// 父進程退出exit(EXIT_SUCCESS);}// 子進程創建新會話pid_t sid = setsid();if (sid < 0) {perror("setsid 失敗");exit(EXIT_FAILURE);}// 改變工作目錄if (chdir("/") < 0) {perror("chdir 失敗");exit(EXIT_FAILURE);}// 設置文件權限掩碼umask(0);// 關閉不需要的文件描述符close(STDIN_FILENO);close(STDOUT_FILENO);close(STDERR_FILENO);// 守護進程的主循環while (1) {// 執行守護進程的任務sleep(1);}return 0;}

關閉標準輸入、標準輸出和標準錯誤輸出的文件描述符,防止守護進程與控制終端交互。

編譯和運行步驟

  1. 把上述代碼保存為 daemon_example.c。
  1. 打開終端,進入代碼所在目錄。
  1. 使用 gcc 編譯代碼:gcc daemon_example.c -o daemon_example。
  1. 運行編譯后的可執行文件:./daemon_example。此時,守護進程會在后臺持續運行。

通過以上步驟,你可以逐步掌握 Linux 多進程的相關知識,包括進程的創建、僵尸進程和孤兒進程的處理,以及守護進程的實現。在實際應用中,多進程編程可以提高程序的并發性能,充分利用多核 CPU 的資源。

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

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

相關文章

MTCNN 人臉識別

前言 此處介紹強大的 MTCNN 模塊&#xff0c;給出demo&#xff0c;展示MTCNN 的 OOP&#xff0c; 以及ROS利用 C 節點&#xff0c;命令行調用腳本執行實際工作的思路。 MTCNN Script import argparse import cv2 from mtcnn import MTCNN import osclass MTCNNProcessor:def…

01_核心系統下的技術原理解析

15年前&#xff0c;基本上國內的核心系統被C壟斷&#xff0c;基本上是IBM的那套東西&#xff0c;場景也是比價復雜&#xff0c;這里不再贅述&#xff0c;TPS太過于龐大&#xff0c;技術上確實比較復雜。為此我這里拋磚引玉&#xff0c;說下對應的支付系統&#xff1a; &#x…

Python 實現最小插件框架

文章目錄 Python 實現最小插件框架1. 基礎實現項目結構plugin_base.py - 插件基類plugins/hello.py - 示例插件1plugins/goodbye.py - 示例插件2main.py - 主程序 2. 更高級的特性擴展2.1 插件配置支持2.2 插件依賴管理2.3 插件熱加載 3. 使用 setuptools 的入口點發現插件3.1 …

電感詳解:定義、作用、分類與使用要點

一、電感的基本定義 電感&#xff08;Inductor&#xff09; 是由導線繞制而成的儲能元件&#xff0c;其核心特性是阻礙電流變化&#xff0c;將電能轉化為磁能存儲。 基本公式&#xff1a; 自感電動勢&#xff1a; E -L * (di/dt) &#xff08;L&#xff1a;電感值&#xff0c…

運行一次性任務與定時任務

運行一次性任務與定時任務 文章目錄 運行一次性任務與定時任務[toc]一、使用Job運行一次性任務1.創建一次性任務2.測試一次性任務3.刪除Job 二、使用CronJob運行定時任務1.創建定時任務2.測試定時任務3.刪除CronJob 一、使用Job運行一次性任務 1.創建一次性任務 &#xff08;…

對話記憶(Conversational Memory)

一、引言 在與大型語言模型&#xff08;LLM&#xff09;交互的場景中&#xff0c;對話記憶&#xff08;Conversational Memory&#xff09;指的是模型能夠在多輪對話中保留、檢索并利用先前上下文信息的能力。這一機制使得對話系統不再僅僅是“問答機”&#xff0c;而是能夠持…

【HD-RK3576-PI】VNC 遠程桌面連接

在當今數字化時代&#xff0c;高效便捷的操作方式是技術愛好者與專業人士的共同追求。對于使用 HD-RK3576-PI微型單板計算機的用戶而言&#xff0c;當面臨沒有顯示屏的場景時&#xff0c;如何實現遠程操作桌面系統呢&#xff1f;別擔心&#xff0c;VNC 遠程桌面連接將為你解決這…

【unity游戲開發介紹之UGUI篇】UGUI概述和基礎使用

注意&#xff1a;考慮到UGUI的內容比較多&#xff0c;我將UGUI的內容分開&#xff0c;并全部整合放在【unity游戲開發介紹之UGUI篇】專欄里&#xff0c;感興趣的小伙伴可以前往逐一查看學習。 文章目錄 前言1、UI系統的重要性2、UGUI概述2.1 基本定義2.2 UGUI發展歷史 3、學習U…

Ubuntu 系統深度清理:徹底卸載 Redis 服務及殘留配置

Ubuntu 系統深度清理&#xff1a;徹底卸載 Redis 服務及殘留配置 在Ubuntu系統中&#xff0c;Redis是一種廣泛使用的內存數據存儲系統&#xff0c;用于緩存和消息傳遞等場景。然而&#xff0c;有時候我們需要徹底卸載Redis&#xff0c;以清理系統資源或為其他應用騰出空間。本…

[ARC196A] Adjacent Delete 題解

假設 n n n 是偶數。如果我們忽略刪除相鄰數的條件&#xff0c;即可以任選兩個數相減&#xff0c;那么答案應該是前 n 2 \frac{n}{2} 2n? 大的數&#xff08;記作“較大數”&#xff09;的和減去前 n 2 \frac{n}{2} 2n? 小的數&#xff08;記作“較小數”&#xff09;的和…

Linux上位機開發實踐(關于Qt的移植)

【 聲明&#xff1a;版權所有&#xff0c;歡迎轉載&#xff0c;請勿用于商業用途。 聯系信箱&#xff1a;feixiaoxing 163.com】 linux平臺上面&#xff0c;很多界面應用&#xff0c;都是基于qt開發的。不管是x86平臺&#xff0c;還是arm平臺&#xff0c;qt使用的地方都比較多。…

”插入排序“”選擇排序“

文章目錄 插入排序1. 直接插入排序(O(n^2))舉例1&#xff1a;舉例2&#xff1a;直插排序的"代碼"直插排序的“時間復雜度” 2. 希爾排序(O(n^1.3))方法一方法二(時間復雜度更優) 選擇排序堆排序直接選擇排序 我們學過冒泡排序&#xff0c;堆排序等等。&#xff08;回…

FPGA_BD Block Design學習(一)

PS端開發流程詳細步驟 1.第一步&#xff1a;打開Vivado軟件&#xff0c;創建或打開一個工程。 2.第二步&#xff1a;在Block Design中添加arm核心&#xff0c;并將其配置為IP核。 3.第三步&#xff1a;配置arm核心的外設信息&#xff0c;如DDR接口、時鐘頻率、UART接口等。 …

【Python] pip制作離線包

制作離線安裝包是一種非常實用的方法&#xff0c;尤其是在網絡環境受限或需要在多臺機器上部署相同環境時。以下是詳細的步驟&#xff0c;幫助您創建一個包含所有依賴項的離線安裝包&#xff0c;并在后續環境中復用。 步驟 1&#xff1a;準備工具和環境 確保您有一臺可以訪問互…

為啥物聯網用MQTT?

前言 都說物聯網用MQTT&#xff0c;那分別使用Http和Mqtt發送“Hello”&#xff0c;比較一下就知道啦 HTTP HTTP請求報文由請求行、頭部字段和消息體組成。一個最簡單的HTTP POST請求如下&#xff1a; POST / HTTP/1.1 Host: example.com Content-Length: 5 Content-Type: …

操作系統 ------ 五種IO模型

阻塞IO&#xff1a;一個IO請求操作&#xff0c;準備階段和復制階段都會阻塞應用程序&#xff0c;直到操作完全完成 非阻塞IO&#xff1a;一個IO操作請求&#xff0c;先判斷準備階段是否完成&#xff0c;如果未完成立即返回&#xff0c;否則&#xff0c;進入復制階段&#xff0…

service和endpoints是如何關聯的?

在Kubernetes中&#xff0c;Service 和 Endpoints 是兩個密切關聯的對象&#xff0c;它們共同實現了服務發現和負載均衡的功能。以下是它們之間的關聯和工作原理&#xff1a; 1. Service 的定義 Service 是一種抽象&#xff0c;定義了一組邏輯上相關的 Pod&#xff0c;以及用…

程序化廣告行業(78/89):多因素交織下的行業剖析與展望

程序化廣告行業&#xff08;78/89&#xff09;&#xff1a;多因素交織下的行業剖析與展望 在程序化廣告這片充滿活力又不斷變化的領域&#xff0c;持續學習和知識共享是我們緊跟潮流、實現突破的關鍵。一直以來&#xff0c;我都渴望能與大家一同探索這個行業的奧秘&#xff0c…

數智化重構供應商管理

當供應鏈韌性成為核心競爭力&#xff0c;你的供應商管理還在 “摸著石頭過河” 嗎&#xff1f; 在傳統模式下&#xff0c;供應商管理高度依賴人工經驗與紙質流程&#xff1a; 入庫篩選如“大海撈針”&#xff1a;供應商資質審核停留在Excel表格比對&#xff0c;資質造假、歷史…

網絡互連與互聯網

1.在路由表中找不到目標網絡時使用默認路由&#xff0c;默認路由通常指本地網關的地址。 2.OSPF最主要的特征是使用分布式鏈路狀態協議&#xff0c;而RIP使用的是距離向量協議。 3.OSPF使用鏈路狀態公告LSA擴散路由信息 4.內部網關路由協議IGRP是一種動態距離矢量路由協議&a…