守護進程編程

守護進程編程

守護進程的含義

定義

守護進程(Daemon Process)是在后臺運行的進程,它獨立于控制終端并且周期性地執行某種任務或等待處理某些發生的事件。守護進程是一種很有用的進程,它在系統后臺運行,為系統或其他進程提供服務,而用戶通常不會直接與它交互。

特點

(1)獨立于終端

守護進程沒有控制終端。它不會因為終端的關閉而停止運行。例如,一個網絡服務器守護進程,它在后臺監聽網絡請求,無論用戶是否登錄終端,它都能正常工作。這使得守護進程能夠持續運行,不受用戶登錄狀態的限制。

(2)生命周期長

守護進程通常在系統啟動時開始運行,并且會一直運行,直到系統關閉。它們的生命周期與系統的運行時間緊密相關。比如,系統日志守護進程(如 syslogd)從系統啟動開始就記錄日志,直到系統關閉,它一直都在后臺工作,記錄各種系統事件的日志信息。

(3)提供服務

守護進程的主要功能是為系統或其他進程提供服務。這些服務可以是網絡服務(如 Web 服務器守護進程 Apache)、打印服務(如 CUPS 打印守護進程)、定時任務(如 cron 守護進程)等。它們在后臺默默運行,確保系統功能的正常實現。

編程實現一個守護進程的主要過程

1. 創建子進程并退出父進程

  • 使用 fork() 創建一個子進程。
  • 父進程退出,確保守護進程與終端分離。

2. 創建新的會話

  • 調用 setsid() 創建一個新的會話,使守護進程成為會話的首進程。
  • 這一步可以確保守護進程與終端完全分離。

3. 改變工作目錄

  • 將工作目錄改為根目錄(/),避免守護進程依賴于特定的用戶目錄。
  • 防止守護進程在用戶注銷時被意外終止。
  1. 關閉所有文件描述符
  • 關閉所有打開的文件描述符,避免資源泄漏。
  • 防止守護進程意外地向終端輸出信息。
  1. 設置文件權限掩碼
  • 設置文件權限掩碼(umask),確保守護進程創建的文件具有合適的權限。
  1. 打開日志文件
  • 打開日志文件,用于記錄守護進程的運行狀態。
  1. 進入主循環
  • 守護進程進入主循環,執行其主要功能。
  • 例如,定期記錄當前時間到日志文件。

創建一個守護進程一般有 nohup命令、fork()函數和 daemon()函數三種方法,請分別在阿里云服務器、樹莓派上用這三種方式創建一個守護進程。

阿里云

nohup命令

我們以這個簡單的python腳本為例,它每隔10秒鐘記錄一次當前時間到日志文件中


import time
def main():while True:print(f"{time.strftime('%Y-%m-%d %H:%M:%S')} - 守護進程正在運行")time.sleep(10)
if __name__ == "__main__":main()

使用以下命令運行程序


nohup python3 demo.py &python3 demo.py

在這里插入圖片描述

fork()
使用 fork() 創建守護進程的步驟

第一次 fork():
創建一個子進程,父進程退出。
這樣可以確保子進程與終端分離。
創建新的會話:
調用 setsid() 創建一個新的會話,使子進程成為會話的首進程。
這一步可以確保子進程與終端完全分離。
第二次 fork():
再次創建一個子進程,確保守護進程不能重新打開控制終端。
父進程退出,子進程繼續運行。
改變工作目錄:
將工作目錄改為根目錄(/),避免守護進程依賴于特定的用戶目錄。
關閉所有文件描述符:
關閉所有打開的文件描述符,避免資源泄漏。
設置文件權限掩碼:
設置文件權限掩碼(umask),確保守護進程創建的文件具有合適的權限。
進入主循環:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <time.h>
#include <string.h>
#include <errno.h>#define LOG_FILE "/tmp/shouhufork.txt"void log_message_to_file(const char *message) {int log_fd = open(LOG_FILE, O_WRONLY | O_APPEND | O_CREAT, 0644);if (log_fd < 0) {perror("Failed to open log file");return;}write(log_fd, message, strlen(message));write(log_fd, "\n", 1);close(log_fd);
}int main() {pid_t pid;// Step 1: First forkpid = fork();if (pid < 0) {perror("First fork failed");exit(EXIT_FAILURE);}if (pid > 0) {// Parent process exitsexit(EXIT_SUCCESS);}// Step 2: Create a new sessionif (setsid() < 0) {perror("Setsid failed");exit(EXIT_FAILURE);}// Step 3: Second forkpid = fork();if (pid < 0) {perror("Second fork failed");exit(EXIT_FAILURE);}if (pid > 0) {// Parent process exitsexit(EXIT_SUCCESS);}// Step 4: Change the working directory to rootif (chdir("/") < 0) {perror("Chdir failed");exit(EXIT_FAILURE);}// Step 5: Close all file descriptorsfor (int i = 0; i < sysconf(_SC_OPEN_MAX); i++) {close(i);}// Step 6: Set the file permission maskumask(0);// Step 7: Open the log filelog_message_to_file("Daemon started successfully");// Step 8: Enter the main loopwhile (1) {time_t now = time(NULL);char *time_str = ctime(&now);char log_message[128];snprintf(log_message, sizeof(log_message), "Current time: %s", time_str);log_message_to_file(log_message);sleep(60); // Sleep for 60 seconds}return 0;
}

將代碼保存為shouhufork.c
使用一下命令編譯代碼:

gcc -o shouhufork shouhufork.c

使用以下命令

./shouhufork

守護進程進入主循環,執行其主要功能。
使用命令

nohup ./shouhufork &
cat nohup.out

在這里插入圖片描述

daemon函數
使用 daemon() 函數創建守護進程的步驟

調用 daemon() 函數:
daemon() 函數會自動完成以下操作:

  • 創建一個子進程,父進程退出。
  • 創建一個新的會話,使子進程成為會話的首進程。
  • 改變工作目錄到根目錄(/)。
  • 關閉所有文件描述符。
  • 設置文件權限掩碼(umask)。

進入主循環:
守護進程進入主循環,執行其主要功能。

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <time.h>
#include <string.h>
#include <errno.h>#define LOG_FILE "/tmp/daemon_log.txt"void log_message_to_file(const char *message) {int log_fd = open(LOG_FILE, O_WRONLY | O_APPEND | O_CREAT, 0644);if (log_fd < 0) {perror("Failed to open log file");return;}write(log_fd, message, strlen(message));write(log_fd, "\n", 1);close(log_fd);
}int main() {// Step 1: Call daemon() functionif (daemon(0, 0) == -1) {perror("Failed to start daemon");exit(EXIT_FAILURE);}// Step 2: Open the log filelog_message_to_file("Daemon started successfully");// Step 3: Enter the main loopwhile (1) {time_t now = time(NULL);char *time_str = ctime(&now);char log_message[128];snprintf(log_message, sizeof(log_message), "Current time: %s", time_str);log_message_to_file(log_message);sleep(60); // Sleep for 60 seconds}return 0;
}

保存為daemon.c,使用以下命令編譯代碼

gcc -o daemon daemon.c

使用以下命令運行

./daemon

在這里插入圖片描述

GDB調試

用法介紹

程序暫停與斷點
斷點(Breakpoint):

  • GDB 允許用戶在程序的特定位置設置斷點。當程序運行到斷點時,它會自動暫停。
  • 斷點可以設置在函數入口、特定行號或特定地址。
    示例
(gdb) break main
(gdb) break file.c:42

暫停程序:

  • 當程序運行到斷點時,GDB 會暫停程序的執行,允許用戶檢查程序的狀態。
  • 用戶可以查看變量的值、調用棧、寄存器內容等。
    示例
(gdb) step  # 進入函數內部
(gdb) next  # 不進入函數內部
(gdb) continue  # 繼續運行到下一個斷點

單步執行
單步執行(Step):

  • GDB 提供了單步執行功能,允許用戶逐行或逐指令執行程序。
  • 單步執行可以幫助用戶觀察程序的執行流程,檢查變量的變化。
    示例
(gdb) step  # 進入函數內部
(gdb) next  # 不進入函數內部
(gdb) continue  # 繼續運行到下一個斷點

查看變量和內存
查看變量:
GDB 允許用戶查看和修改程序中的變量值。
示例

(gdb) print x
(gdb) print *ptr
(gdb) set x = 10

查看內存:
GDB 可以查看和修改內存中的內容。
示例:

(gdb) x/10gx 0x10000000  # 查看從地址 0x10000000 開始的 10 個 8 字節數據
(gdb) set {int}0x10000000 = 42  # 修改內存中的值

調用棧
查看調用棧(Backtrace):
GDB 可以顯示當前程序的調用棧,幫助用戶了解程序的執行路徑。
示例:

(gdb) backtrace

切換棧幀:
用戶可以切換到不同的棧幀,查看不同函數中的變量。
示例

(gdb) frame 2  # 切換到第 2 個棧幀

信號處理
信號(Signal):

  • GDB 可以捕獲和處理程序中的信號(如 SIGSEGV、SIGABRT 等)。
  • 用戶可以設置信號的處理方式,或者在信號發生時暫停程序。

示例:

(gdb) handle SIGSEGV stop
(gdb) handle SIGSEGV nostop

多線程支持
多線程調試:
GDB 支持多線程程序的調試,可以查看和切換不同的線程。
示例:

(gdb) info threads  # 查看所有線程
(gdb) thread 2  # 切換到第 2 個線程

遠程調試
遠程調試:
GDB 支持遠程調試,可以通過網絡連接到運行在其他機器上的程序。
示例:

(gdb) target remote :1234  # 連接到本地端口 1234
工作原理

啟動 GDB:
用戶啟動 GDB 并加載要調試的程序:

gdb ./my_program

設置斷點:
用戶在程序的特定位置設置斷點:

(gdb) break main

運行程序

(gdb) run

暫停程序:
當程序運行到斷點時,GDB 暫停程序的執行。
檢查程序狀態:
用戶可以查看變量、調用棧、內存等信息:

(gdb) print x
(gdb) backtrace

單步執行:
用戶可以逐行或逐指令執行程序:

(gdb) step
(gdb) next

繼續運行

用戶可以繼續運行程序到下一個斷點

(gdb) continue

退出 GDB:
用戶可以退出 GDB:

(gdb) quit
使用gdb調試一個程序

(1)創建 test.c

#include <stdio.h>int multiply(int x, int y) {return x * y;}int divide(int x, int y) {if (y == 0) {fprintf(stderr, "Error: Division by zero\n");return 0;}return x / y;}int main() {int a = 10, b = 0, c = 20, d;d = multiply(a, c);printf("Multiply result: %d\n", d);d = divide(a, b);printf("Divide result: %d\n", d);return 0;

在 main 函數中,變量 a 被初始化為 10,b 被初始化為 0,c 被初始化為 20,d 未初始化
調用 multiply(a, c) 計算 a 和 c 的乘積,即 10 * 20,結果為 200。這個結果被賦值給 d,所以此時 d 的值為 200。接下來打印 Multiply result: 200。然后調用 divide(a, b) 計算 a 和 b 的商,即 10 / 0。由于 b 的值為 0,這將導致除以零的錯誤。divide 函數會打印錯誤信息 “Error: Division by zero” 并返回
0。這個結果被賦值給 d,所以此時d的值變為0.

(2)編譯帶調試信息

gcc -g test.c -o test # -g選項生成調試符號

在這里插入圖片描述

(3)啟動 gdb 調試

gdb ./test
在這里插入圖片描述

(4)設置斷點

break multiply

break divide

在這里插入圖片描述

(5)運行程序

run

在這里插入圖片描述

(6)單步執行

next

在這里插入圖片描述

一直next,直到出現divide函數,執行step命令進入到divide含糊內部進行單步調試

step

在這里插入圖片描述

使用print命令來檢查傳入 divide函數的參數想和y的值,確保他們的預期值

print x

print y

在這里插入圖片描述

(7)單步執行
step
在這里插入圖片描述

?

程序已經執行了 divide 函數中的 if (y == 0) 條件檢查。由于 y 的值是 20(不等于0) 程序將繼續執行 if 語句塊之外的代碼,繼續單步執行step

step?
在這里插入圖片描述

程序已經執行到了 fprintf(stderr, “Error: Division by zero\n”); 這一行,因為在 divide 函數中檢測到了除以零的情況,GDB 顯示了 fprintf 函數的調用信息

(8)檢查d的輸出值(d在main函數里面,要檢查d的值就要退出divide函數并返回到調用點,使用finish命令)

finish
在這里插入圖片描述

(9)繼續執行程序(程序將繼續執行并打印 Divide result: 后跟 d 的值)
continue
在這里插入圖片描述

?
完成調試,退出gdb時,使用quit命令

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

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

相關文章

在復雜性的迷宮里尋找路標 —— 讀《人月神話》有感

初讀《人月神話》時&#xff0c;正值參與的第一個大型項目陷入泥潭&#xff1a;需求像不斷膨脹的氣球&#xff0c;團隊規模從 10 人擴充到 30 人&#xff0c;進度卻像被灌了鉛的鐘表&#xff0c;指針越來越沉重。布魯克斯在書中寫下的 "向進度落后的項目增加人力&#xff…

SpringCloud Alibaba微服務工程搭建

前言 在講微服務工程的搭建之前&#xff0c;我們先分析下為什么要使用微服務呢&#xff1f; 1、單體應用的痛點 維護困難&#xff1a;代碼臃腫&#xff0c;牽一發而動全身。擴展性差&#xff1a;無法按需擴展特定功能&#xff0c;只能整體擴容。技術棧僵化&#xff1a;難以引…

flutter json解析增強

依賴:xxf_json 反序列化兼容特征一覽表 類型\是否兼容 int double num string bool int yes yes yes yes yes double yes yes yes yes yes num yes yes yes yes yes string yes yes yes yes yes bool yes yes yes yes yes 專業詞語 .g…

Neo4j初解

Neo4j 是目前應用非常廣泛的一款高性能的 NoSQL 圖數據庫&#xff0c;其設計和實現專門用于存儲、查詢和遍歷由節點&#xff08;實體&#xff09;、關系&#xff08;邊&#xff09;以及屬性&#xff08;鍵值對&#xff09;構成的圖形數據模型。它的核心優勢在于能夠以一種自然且…

學習MySQL的第十天

一、MySQL的數據類型 1.MySQL的數據類型 2.常見的數據類型的屬性 二、整數類型 三、浮點類型 REAL默認就是DOUBLE。如果你把SQL模式設定為啟用“REAL_AS_FLOAT”,那么,MySQL就認為REAL是FLOAT。如果要啟用“REAL_AS_FLOAT”,可以通過以下SQL語句實現: SET sql_mode &…

ubuntu24.04上使用qemu+buildroot+uboot+linux+tftp+nfs模擬搭建vexpress-ca9嵌入式linux開發環境

1 準備工作 1.1 安裝依賴工具 sudo apt-get update && sudo apt-get install build-essential git bc flex libncurses5-dev libssl-dev device-tree-compiler1.2 安裝arm交叉編譯工具鏈 sudo apt install gcc-arm-linux-gnueabihf安裝之后&#xff0c;在終端輸入ar…

ubuntu 22.04 使用ssh-keygen創建ssh互信賬戶

現有兩臺ubuntu 22.04服務器&#xff0c;ip分別為192.168.66.88和192.168.88.66。需要將兩臺服務器創建新用戶并將新用戶做互信。 創建賬戶 adduser user1 # 如果此用戶不想使用密碼&#xff0c;直接一直回車就行&#xff0c;創建的用戶是沒法使用用戶密碼進行登陸的 su - …

【PCIE配置空間】

1 PCIE配置空間 1.1 軟件如何知道PCIE設備是Swith&#xff0c;RC還是EP&#xff1f; –軟件通過讀取寄存器信息。 PCIE配置空間? PCIE寄存器&#xff1b;--PCIE配置協議規定必須實現的空間。--PCIE存在兩種配置空間Type0/Type1;--Type0配置空間EP設備必須實現&#xff1b;-…

Android 熱點二維碼簡單示例

Android 熱點二維碼簡單示例 一、前言 Android 原生設置有熱點二維碼分享功能&#xff0c;有些系統應用也會有這個需求。 下面看看是如何實現的。 本文是一個比較簡單的內容。 二、熱點二維碼生成實現 1、效果 整個應用就一個普通的Activity&#xff0c;顯示一個按鈕和二維…

uv:重新定義Python開發效率的下一代工具鏈

在Python生態系統中&#xff0c;包管理和項目工具鏈的復雜性一直是開發者面臨的一大挑戰。從依賴管理、虛擬環境創建到多版本Python切換&#xff0c;傳統的工具鏈&#xff08;如pip、virtualenv、poetry等&#xff09;雖然功能強大&#xff0c;但操作繁瑣、性能不足的問題長期存…

T101D加固平板電腦:無人機地面站的高效智能控制核心

隨著無人機技術在應急救援、農業監測、軍事偵察等領域的廣泛應用&#xff0c;對地面控制設備的要求也日益提高。魯成偉業推出的T101D加固平板電腦憑借其高性能、強防護和專業化設計&#xff0c;成為無人機地面站的核心控制終端&#xff0c;為復雜環境下的作業提供了可靠支持。 …

Datawhale AI春訓營】AI + 新能源(發電功率預測)Task1

賽題鏈接 官網 新能源發電功率預測賽題進階方案 下面是ai給的一些建議 新能源發電功率預測賽題進階方案 一、時序特性深度挖掘 1. 多尺度周期特征 # 分鐘級周期編碼 train[15min_index] (train[hour]*4 train[minute]//15)# 周周期特征 train[weekday] pd.to_datetime…

山東科技大學深度學習考試回憶

目錄 一、填空&#xff08;五個空&#xff0c;十分&#xff09; 二、選擇題(五個&#xff0c;十分&#xff09; 三、判斷題&#xff08;五個&#xff0c;五分&#xff09; 四、論述題&#xff08;四個&#xff0c;四十分&#xff09; 五、計算題&#xff08;二個&#xff…

Redis線上操作最佳實踐有哪些?

大家好&#xff0c;我是鋒哥。今天分享關于【Redis線上操作最佳實踐有哪些?】面試題。希望對大家有幫助&#xff1b; Redis線上操作最佳實踐有哪些? 1000道 互聯網大廠Java工程師 精選面試題-Java資源分享網 在使用 Redis 時&#xff0c;尤其是在生產環境中&#xff0c;合理…

mac中的zip文件壓縮與壓縮文件中指定目錄刪除

問題 在使用mac的圖形界面壓縮文件后&#xff0c;往往那個壓縮文件中帶有__MACOSX文件&#xff0c;但是&#xff0c;這個文件夾又是我們不需要的目錄&#xff0c;所有&#xff0c;需要對mac圖形化界面壓縮后的文件目錄進行刪除&#xff0c;改如何做&#xff1f; 檢查壓縮文件…

【記錄】服務器用命令開啟端口號

這里記錄下如何在服務器上開啟適用于外界訪問的端口號。 方法 1 使用防火墻 1 su &#xff0c;命令 輸入密碼 切換到root節點 2 開啟防火墻 systemctl start firewalld3 配置開放端口 firewall-cmd --zonepublic --add-port8282/tcp --permanent4 重啟防火墻 firewall-cmd…

深度學習-torch,全連接神經網路

3. 數據集加載案例 通過一些數據集的加載案例&#xff0c;真正了解數據類及數據加載器。 3.1 加載csv數據集 代碼參考如下 import torch from torch.utils.data import Dataset, DataLoader import pandas as pd ? ? class MyCsvDataset(Dataset):def __init__(self, fil…

C++/Python實現RGB和HSI相互轉換

1--C版本 #include <opencv2/opencv.hpp> #include <iostream> #include <cmath>// RGB to HSI cv::Vec3f RGBtoHSI(cv::Vec3b rgb) {float B rgb[0] / 255.0f;float G rgb[1] / 255.0f;float R rgb[2] / 255.0f;float num 0.5f * ((R - G) (R - B));f…

【Linux我做主】make和makefile自動化構建

make和makefile自動化構建 make和makefile自動化構建github地址前言背景介紹為什么需要make和makefile&#xff1f; make和makefile解析什么是make和makefile依賴關系和依賴方法核心語法結構簡單演示編譯清理 多階段編譯示例 make時執行的順序場景1&#xff1a;clean目標在前(特…

Qt 入門 5 之其他窗口部件

Qt 入門 5 之其他窗口部件 本文介紹的窗口部件直接或間接繼承自 QWidget 類詳細介紹其他部件的功能與使用方法 1. QFrame 類 QFrame類是帶有邊框的部件的基類。它的子類包括最常用的標簽部件QLabel另外還有 QLCDNumber、QSplitter,QStackedWidget,QToolBox 和 QAbstractScrol…