Linux進(jìn)程脫離父進(jìn)程控制設(shè)置過程
在Linux中,要使進(jìn)程完全脫離父進(jìn)程控制,通常需要使用以下系統(tǒng)調(diào)用組合:
1.setsid() - 創(chuàng)建新會(huì)話
這是最關(guān)鍵的一步,它會(huì):
- 創(chuàng)建一個(gè)新的會(huì)話
- 使調(diào)用進(jìn)程成為會(huì)話首進(jìn)程
- 使調(diào)用進(jìn)程成為新進(jìn)程組的組長(zhǎng)
- 使調(diào)用進(jìn)程脫離控制終端
#include <unistd.h>
#include <stdio.h>
int main() {
pid_t pid = fork();
if (pid < 0) {
perror("fork failed");
return 1;
}
// 父進(jìn)程退出
if (pid > 0) {
_exit(0);
}
// 子進(jìn)程創(chuàng)建新會(huì)話
if (setsid() < 0) {
perror("setsid failed");
return 1;
}
// 此時(shí)進(jìn)程已脫離原會(huì)話和控制終端
// 繼續(xù)執(zhí)行守護(hù)進(jìn)程邏輯...
return 0;
}
2.setpgid() - 設(shè)置進(jìn)程組ID
用于更精確地控制進(jìn)程組關(guān)系:
#include <unistd.h>
#include <stdio.h>
int main() {
pid_t pid = fork();
if (pid < 0) {
perror("fork failed");
return 1;
}
if (pid == 0) { // 子進(jìn)程
// 將自己放入新的進(jìn)程組(通常與setsid配合)
if (setpgid(0, 0) < 0) {
perror("setpgid failed");
return 1;
}
// 然后創(chuàng)建新會(huì)話
if (setsid() < 0) {
perror("setsid failed");
return 1;
}
} else { // 父進(jìn)程
// 父進(jìn)程可以等待子進(jìn)程設(shè)置完成
sleep(1);
// 父進(jìn)程退出,子進(jìn)程被init進(jìn)程接管
}
return 0;
}
3.完整的守護(hù)進(jìn)程實(shí)現(xiàn)
#include <unistd.h>
#include <sys/stat.h>
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
void daemonize() {
pid_t pid;
// 1. 創(chuàng)建子進(jìn)程,父進(jìn)程退出
pid = fork();
if (pid < 0) {
perror("fork");
exit(EXIT_FAILURE);
}
if (pid > 0) { // 父進(jìn)程
exit(EXIT_SUCCESS);
}
// 2. 創(chuàng)建新會(huì)話,脫離控制終端
if (setsid() < 0) {
perror("setsid");
exit(EXIT_FAILURE);
}
// 3. 改變工作目錄到根目錄
if (chdir("/") < 0) {
perror("chdir");
exit(EXIT_FAILURE);
}
// 4. 重設(shè)文件權(quán)限掩碼
umask(0);
// 5. 關(guān)閉所有文件描述符
for (int i = 0; i < sysconf(_SC_OPEN_MAX); i++) {
close(i);
}
// 6. 重定向標(biāo)準(zhǔn)輸入輸出到/dev/null
int fd = open("/dev/null", O_RDWR);
if (fd != -1) {
dup2(fd, STDIN_FILENO);
dup2(fd, STDOUT_FILENO);
dup2(fd, STDERR_FILENO);
if (fd > 2) {
close(fd);
}
}
}
int main() {
daemonize();
// 守護(hù)進(jìn)程的主要邏輯
while (1) {
// 執(zhí)行任務(wù)
sleep(10);
}
return 0;
}
4.使用 systemd 服務(wù)的現(xiàn)代方法
對(duì)于現(xiàn)代Linux系統(tǒng),推薦使用systemd服務(wù):
# /etc/systemd/system/my-service.service [Unit] Description=My Daemon Service After=network.target [Service] Type=forking PIDFile=/var/run/my-service.pid ExecStart=/usr/local/bin/my-daemon Restart=always User=nobody Group=nogroup # 確保進(jìn)程完全獨(dú)立 NoNewPrivileges=yes PrivateTmp=yes ProtectSystem=strict ProtectHome=true PrivateDevices=yes [Install] WantedBy=multi-user.target
5.關(guān)鍵區(qū)別
| 系統(tǒng)調(diào)用 | 作用 | 效果 |
|---|---|---|
| setsid() | 創(chuàng)建新會(huì)話 | 進(jìn)程成為會(huì)話首進(jìn)程,脫離控制終端 |
| setpgid(pid, pgid) | 設(shè)置進(jìn)程組 | 將指定進(jìn)程放入指定進(jìn)程組 |
| fork() + setsid() | 標(biāo)準(zhǔn)守護(hù)進(jìn)程 | 完全脫離父進(jìn)程控制 |
6.注意事項(xiàng)
- 雙重fork技巧:某些情況下需要雙重fork確保進(jìn)程不會(huì)成為會(huì)話首進(jìn)程
- 信號(hào)處理:重新設(shè)置信號(hào)處理器,特別是SIGHUP
- 資源清理:關(guān)閉不需要的文件描述符
- 工作目錄:更改到安全目錄,防止占用文件系統(tǒng)
- 日志記錄:使用syslog或其他日志機(jī)制
這樣設(shè)置的進(jìn)程將完全獨(dú)立于原始父進(jìn)程,即使父進(jìn)程退出也不會(huì)影響它,并由init/systemd進(jìn)程接管。
7.總結(jié)
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
在Linux 服務(wù)器下修改防火墻允許該端口遠(yuǎn)程訪問的方法
今天小編就為大家分享一篇在Linux 服務(wù)器下修改防火墻允許該端口遠(yuǎn)程訪問的方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2019-07-07
Centos中TCPWrappers訪問控制實(shí)現(xiàn)
這篇文章主要介紹了Centos中TCPWrappers訪問控制實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-11-11
VPS CentOS-6 下 LNMP HTTP web服務(wù)器的搭建步驟
雖然現(xiàn)在很多朋友比較喜歡使用軍哥的lnmp一鍵安裝包,但手工配置方法也要會(huì),否則容易技術(shù)退步2014-06-06
crontab定時(shí)任務(wù)不執(zhí)行的原因分析與解決方法
這篇文章主要給大家介紹了關(guān)于crontab定時(shí)任務(wù)不執(zhí)行的原因分析與解決方法,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-01-01
Linux Bash腳本自動(dòng)創(chuàng)建keystore和生成公鑰方式
該文章介紹了如何使用Bash腳本自動(dòng)化生成新的keystore文件并提取公鑰的過程,腳本通過獲取腳本絕對(duì)路徑、使用關(guān)聯(lián)數(shù)組和函數(shù)傳遞數(shù)組參數(shù)等方法來實(shí)現(xiàn)自動(dòng)化,并且可以在Windows上使用Git Bash運(yùn)行2025-11-11

