让你的C#程序成为守护进程
起因
本文主要是通过P/Invoke的方式调用系统API,让c#程序成为守护进程.
使用pstree查看进程间的关系
C#调用系统API实现守护进程
using System;
using System.Runtime.InteropServices;
using System.Threading;
namespace linuxapp
{
    class MyDaemon
    {
        [DllImport("libc", SetLastError = true)]
        private static extern int close(int fd);
        [DllImport("libc", SetLastError = true)]
        private static extern int fork();
        [DllImport("libc", SetLastError = true)]
        private static extern int exit(int status);
        [DllImport("libc", SetLastError = true)]
        private static extern int setsid();
        [DllImport("libc", SetLastError = true)]
        private static extern int chdir(string path);
        [DllImport("libc", SetLastError = true)]
        private static extern int umask(int cmask);
        [DllImport("libc", SetLastError = true)]
        private static extern int syslog(int priority, string message);
        //将syslog.h文件中, log宏定义改为常量
        const int LOG_EMERG = 0;	/* system is unusable */
        const int LOG_ALERT = 1;	/* action must be taken immediately */
        const int LOG_CRIT = 2;	        /* critical conditions */
        const int LOG_ERR = 3;	        /* error conditions */
        const int LOG_WARNING = 4;	/* warning conditions */
        const int LOG_NOTICE = 5;	/* normal but significant condition */
        const int LOG_INFO = 6;	        /* informational */
        const int LOG_DEBUG = 7;	/* debug-level messages */
        /// <summary>
        /// 设置为守护进程程序
        /// </summary>
        /// <returns></returns>
        static int SetDaemon()
        {
            int pid = fork();  //fork创建当前进程的副本
            if (pid < 0)
            {
                return -1;
            }
            if (pid > 0)
            {
                exit(0);       //让父进程退出,让子进程成为孤儿进程,由系统进程领养,可以通过pstree查看
            }
            else
            {
                setsid();      //设置一个新的会话
                chdir("/");    //设置当前程序的工作目录为根目录
                umask(0);      //设置umask为0
                close(0);      //关闭标准输入文件STDIN_FILENO
                close(1);      //关闭标准输出文件STDOUT_FILENO
                close(2);      //关闭标准错误输出文件STDERR_FILENO
            }
            return 0;
        }
        /// <summary>
        /// 真正要执行的实现代码
        /// 由于在SetDaemon函数中,将标准的输入输出文件及错误文件都关闭了,所以在Run函数中,是不能直接使用Console.WriteLine,可以使用:
        /// 1.将日志信息通过syslog输出到系统日志中
        /// 2.将日志信息写入到当前程序的日志(可使用nlog等日志组件)
        /// </summary>
        static void Run()
        {
            //这里只是模拟
            while (true)
            {
                Thread.Sleep(30 * 1000);     //休眠30秒,执行一次
                syslog(LOG_INFO, "MyDaemon(csharp) is running");  //记录日志,查看cat /var/log/messages
            }
        }
        static void Main(string[] args)
        {
            int result = SetDaemon();
            if (result == -1)
            {
                return;
            }
            Run();
        }
    }
}编译和运行(使用Mono)
#mcs 是mono的编译器
mcs MyDaemon.cs   
#通过mono运行c#程序
mono MyDaemon.exe查看进程关系和日志
#pstree 查看进程树
pstree#查看系统日志(应该是Centos,后面换为Ubuntu Server)
cat /var/log/messages#如何关闭守护进程
#1.先查找mono的进程id
ps -u root(当前用户) | grep mono
#2,根据进程id,关闭进程
kill -9 18197支持dotnet core及dotnet 5/6(2021-08-10)
在2017年,.Net Core还不够火,即使在2021年,还是有一部分项目还没有迁移到.Net Core上,更别说迁移到.Net 5,其实上边守护进程的代码,不做任何改变就可以在.Net 5/6上运行.
尤其到.Net 5/6上,使用更方便,因为可以发布单文件.不用安装.Net 运行时/也不用安装Mono.还有一种就是NativeAOT这个暂不支持交叉编译.如果需要NativeAOT的可以参考这篇:
//在.Net 6 生成Native库,让C/C++调用 
//https://www.qiufengblog.com/articles/dotnet-6-natvie-invoke.html生成单文件及裁剪之后的文件大小:
生成单文件及裁剪的工程文件配置:
<PropertyGroup>
  <OutputType>Exe</OutputType>
  <TargetFramework>net6.0</TargetFramework>
  <!--发布时,生成单文件-->
    <PublishSingleFile>true</PublishSingleFile>
  <!--发布时,进行裁剪不需要的dll-->
  <PublishTrimmed>true</PublishTrimmed> 
  <TrimMode>link</TrimMode>
  <SuppressTrimAnalysisWarnings>false</SuppressTrimAnalysisWarnings>
</PropertyGroup>将文件上传到Linux服务器上:
#因为文件是在Windows生成的,在Linux上没法运行,要给文件可执行权限
chmod +x Daemon
#继续使用pstree查看Linux进程树
pstree具体有没有运行成功,还是查看一下系统日志有没有写入成功.
由于Ubuntu 20.04系统日志文件名换为:syslog
#ubuntu 20.04查看系统日志
cat /var/log/syslog相关文章
- Spring Boot中对接Twilio以实现发送验证码和验证短信码
- Spring Boot 3.5:这次更新让你连配置都不用写了,惊不惊喜?
- Spring Boot+Pinot实战:毫秒级实时竞价系统构建
- SpringBoot敏感配置项加密与解密实战
- SpringBoot 注解最全详解,建议收藏!
- Spring Boot 常用注解大全:从入门到进阶
- SpringBoot启动之谜:@SpringBootApplication如何让配置化繁为简
- Springboot集成Kafka原理_spring集成kafka的原理
- Spring Boot中@Data注解的深度解析与实战应用
- 大佬用1000字就把SpringBoot的配置文件讲的明明白白!

