初探守护进程,使用setsid()创建守护进程

本文详细介绍了守护进程(Daemon)的概念及用途,包括其作为系统服务在后台运行的特点,并提供了创建守护进程的具体步骤和示例代码。此外,还解释了两次fork的目的及setsid函数的作用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

守护进程介绍

Daemon()程序是一直运行的服务端程序,又称为守护进程。通常在系统后台运行,没有控制终端,不与前台交互,Daemon程序一般作为系统服务使用。Daemon是长时间运行的进程,通常在系统启动后就运行,在系统关闭时才结束。一般说Daemon程序在后台运行,是因为它没有控制终端,无法和前台的用户交互。Daemon程序一般都作为服务程序使用,等待客户端程序与它通信。我们也把运行的Daemon程序称作守护进程。

守护进程是自成会话组,并且已经脱离终端而独立运行.Linux的多大服务器就是用守护进程实现的,例如ftp服务器,ssh服务器,wed服务器等,守护进程还有一个作用是可以完成作业规划进程crond
通常在使用ps命令查看进程时 需要加上 x参数

ps axj | more

创建守护进程

使用的函数是


#include<unistd.h>

pid_t setsid(void)

百度了好久,对于这个函数的解释还是模棱两可, 结果最清晰的解释还是man手册 = =

setsid() creates a new session if the calling process is not a process group leader. The calling process is the leader of the new session, the process group leader of the new process group, and has no controlling terminal. The process group ID and session ID of the calling process are set to the PID of the calling process. The calling process will be the only process in this new process group and in this new session.

setsid()作用是创建一个新的会话, 调用进程不能是当前进程组的组长. 调用函数的进程就是新会话中的话首进程(leader) ,也是新会话中进程组的组长(leader),调用函数后此进程会脱离当前的控制终端,
当前的会话ID和进程组ID被设定为调用此函数的进程的PID, 同时调用此函数的进程将会是这个进程组,或者说是这个会话中的唯一进程.(自成一派)

代码

#include<stdio.h>
#include<unistd.h>
#include<signal.h>
#include<stdlib.h>
#include<fcntl.h>
#include<sys/stat.h>
void mydaemon()
{
    int i;
    int fd0;
    pid_t pid;
    struct sigaction sa;
    umask(0);
    if( ( pid = fork() ) < 0 )
    {
        perror("fork");
    }
    else if(pid > 0)
    {
        exit(0);
    }
    setsid();
    //忽略子进程退出状态也是处理信号的一种方法
    //避免子进程一直保持僵尸状态
    sa.sa_handler = SIG_IGN;
    sigemptyset(&sa.sa_mask);
    sa.sa_flags = 0;
    if( sigaction(SIGCHLD, &sa, NULL) < 0 )
    {
        return;
    }
    // 二次fork() 防止误操作打开新的终端// 
    if( ( pid = fork() ) < 0 )
    {
       printf("fork error\n");
    }
    if( pid !=  0)
    {
        exit(0);
    }
    if( chdir("/") < 0 )
    {
        printf(" child dir error\n");
    }
    //关闭 0 1 2  stdin stdout stderr 因为守护进程不用和终端交互
    close(0);
    fd0 = open("/dev/null", O_RDWR);
    dup2(fd0,1);
    dup2(fd0,2);
}
int main()
{
    mydaemon();
    while(1)
    {
        sleep(1);
        //本来想让守护进程打印一句话
        //结果发现守护进程已经关闭stdout了
    }
}

(1)调用一次fork的作用:
第一次fork的作用是让shell认为这条命令已经终止,不用作为前台作业挂在终端输入上,(注意在作业中,进程创建出来的子进程不属于作业本身) 还有就是为了后面的setsid服务,因为调用setsid函数的进程不能是进程组组长,如果不fork出子进程,则此时的父进程是进程组组长,就无法调用setsid。当子进程调用完setsid函数之后,子进程是会话组长也是进程组组长,并且脱离了控制终端,此时,不管控制终端如何操作,新的进程都不会收到一些信号使得进程退出。
(2)第二次fork的作用:
虽然当前关闭了和终端的联系,但是后期可能会误操作打开了终端。
只有会话首进程能打开终端设备,也就是再fork一次,再把父进程退出,再次fork的子进程作为守护进程继续运行,保证了该精灵进程不是对话期的首进程,
第二次不是必须的,是可选的,市面上有些开源项目也是fork一次

查看自己创建的守护进程

写好代码编译一下
这里写图片描述
捞一捞我们自己写的守护进程
这里写图片描述
可以看到守护进程的父进程统一为 Init进程 不属于任何一个终端 TTY 显示为 (? )
当我们切换一个用户,发现还能看到此守护进程
这里写图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值