.NET跨平台实践:再谈用C#开发Linux守护进程

Linux守护进程是Linux的后台服务进程,相当于Windows服务,对于为Linux开发服务程序的朋友来说,Linux守护进程相关技术是必不可少的,因为这个技术不仅仅是为了开发守护进程,还可以拓展到多子进程,父子进程,父子进程通讯与控制等方面,是实现Linux大型服务的基础技术之一。

去年我也曾写了一篇关于守护进程的帖子,名字叫《.NET跨平台实践:用C#开发Linux守护进程》,这篇文章的的确确实现了一个Daemon,不过,它有一个弱点,不能运行多线程!

这篇帖子的目的就是进一步完善,让我们写出一个功能完整,可以用于生产环节的基本的守护进程。

先帖代码(假设项目名是daemon):

  1 using System;
  2 using System.Threading;
  3 using System.Timers;
  4 using System.Runtime.InteropServices;
  5 using System.IO;
  6 using System.Text;
  7
  8
  9 /********************************************
 10  * 一个完整的linux daemon示例,作者宇内流云 *
 11  ********************************************/
 12
 13 namespace daemon
 14 {
 15     class Program
 16     {
 17
 18         const string DaemonTag = "--daemon.";
 19         static void Main(string[] args)
 20         {
 21             // 判断是否已经进入Daemon状态,如果是,就直接执行后台主函数
 22             if (string.IsNullOrEmpty(Environment.GetEnvironmentVariable(DaemonTag)) == false)
 23             {
 24                 Environment.SetEnvironmentVariable(DaemonTag, null);
 25                 DaemonMain(args);
 26                 return;
 27             }
 28
 29
 30             // 如果还没有进入daemon状态,就作daemon处理
 31             /////////////////////////////////////////////////////
 32
 33             int pid = fork();
 34             if (pid != 0) exit(0);
 35             setsid();
 36             pid = fork();
 37             if (pid != 0) exit(0);
 38             umask(0);
 39
 40
 41             // 这儿已经进入“守护进程”工作状态了!
 42
 43             // 关闭所有打开的文件描述符
 44             int max = open("/dev/null", 0);
 45             for (var i = 0; i <= max; i++) { close(i); }
 46
 47
 48             // 设置标记,防止重复运行进入
 49             Environment.SetEnvironmentVariable(DaemonTag,"yes");
 50
 51
 52             //为execp参数重组参数
 53             var args1 = args == null ? new string[2] : new string[args.Length + 2];
 54
 55             args1[0] = "MyDaemon";
 56             args1[1] = Path.Combine(Environment.CurrentDirectory, Thread.GetDomain().FriendlyName);
 57
 58             if (args1.Length > 2)
 59             {
 60                 for (var i = 0; i < args.Length; i++)
 61                 { args1[i + 2] = args[i]; }
 62             }
 63
 64
 65             //守护状态下重新加载和运行本程序
 66             execvp("mono", args1);
 67
 68         }
 69
 70
 71         /// <summary>
 72         /// Daemon工作状态的主方法
 73         /// </summary>
 74         /// <param name="aargs"></param>
 75         static void DaemonMain(string[] aargs)
 76         {
 77             //启动一个线程去处理一些事情
 78             (new Thread(DaemonWorkFunct) { IsBackground = true }).Start();
 79
 80
 81             //daemon时,控制台输入、输出流已经关闭
 82             //请不要再用Console.Write/Read等方法
 83
 84             //阻止daemon进程退出
 85             (new AutoResetEvent(false)).WaitOne();
 86
 87         }
 88
 89
 90         static FileStream fs;
 91         static int count = 0;
 92         static void DaemonWorkFunct() {
 93             fs = File.Open("/tmp/daemon.txt", FileMode.OpenOrCreate);
 94             var t = new System.Timers.Timer() { Interval = 1000 };
 95             t.Elapsed += OnElapsed;
 96             t.Start();
 97         }
 98         private static void OnElapsed(object sender, ElapsedEventArgs e)
 99         {
100             var s = DateTime.Now.ToString("yyy-MM-dd HH:mm:ss") + "\n";
101             var b = Encoding.ASCII.GetBytes(s);
102             fs.Write(b, 0, b.Length);
103             fs.Flush();
104
105             count++;
106             if (count > 100) {
107                 fs.Close();
108                 fs.Dispose();
109                 exit(0);
110             }
111
112         }
113
114
115
116         [DllImport("libc", SetLastError = true)]
117         static extern int fork();
118
119         [DllImport("libc", SetLastError = true)]
120         static extern int setsid();
121
122         [DllImport("libc", SetLastError = true)]
123         static extern int umask(int mask);
124
125         [DllImport("libc", SetLastError = true)]
126         static extern int open([MarshalAs(UnmanagedType.LPStr)]string pathname, int flags);
127
128         [DllImport("libc", SetLastError = true)]
129         static extern int close(int fd);
130
131         [DllImport("libc", SetLastError = true)]
132         static extern int exit(int code);
133
134         [DllImport("libc", SetLastError = true)]
135         static extern int execvp([MarshalAs(UnmanagedType.LPStr)]string file, string[] argv);
136
137
138     }
139
140 }

以上代码的工作过程是:判断程序自身是否已经处于daemon(后台服务)状态,如果是,就直接开始具体的服务工作(开启一个线程,每秒向 /tmp/daemon.txt中打印一行字符,100次后退出),如果不是daemon状态,就进入Daemon处理,使之进入daemon工作状态。

以上代码编译后,会生成一个叫 daemon.exe 的程序,当然,这个程序是为linux开发的,不能在windows上运行。现在,我把它放到linux上面,用mono daemon.exe命令启动它。

这时我们可以看到这个程序启动后,控制台上没有任何输出,也没有阻塞控制台,那么,在哪儿能找到它呢?用 ps -ef命令看看,原来它真的已经在后台运行起来了。

再看看这个后台进程是否完成了它的工作:cat /tmp/daemon.txt 查看文件内容:

从生成的文件内容看,这个Daemon服务程序的确按我们的设计意图,每秒钟向/tmp/daemon.txt打印了一行字符。

注:本文为宇内流云原创作品,转载时请注明出处和作者。

时间: 2024-08-05 11:10:53

.NET跨平台实践:再谈用C#开发Linux守护进程的相关文章

C#开发Linux守护进程

C#开发Linux守护进程 Linux守护进程是Linux的后台服务进程,相当于Windows服务,对于为Linux开发服务程序的朋友来说,Linux守护进程相关技术是必不可少的,因为这个技术不仅仅是为了开发守护进程,还可以拓展到多进程,父子进程文件描述符共享,父子进程通讯.控制等方面,是实现Linux大型服务的基础技术之一. 去年我也曾写了一篇关于守护进程的帖子,名字叫<.NET跨平台实践:用C#开发Linux守护进程>,这篇文章的的确确实现了一个Daemon,不过,它有一个弱点,不能运行多

.NET跨平台实践:用C#开发Linux守护进程

Linux守护进程(Daemon)是Linux的后台服务进程,它脱离了与控制终端的关联,直接由Linux init进程管理其生命周期,即使你关闭了控制台,daemon也能在后台正常工作. 一句话,为Linux开发与控制台无关的,需要在后台长时间不间断运行的“服务程序”,Daemon技术是非常重要的. Daemon程序一般用c/c++开发.不过,我今天要讲的,不是怎么用c/c++开发daemon,而是用C#! 一,创建Daemon程序: 用VS新建一个控制台项目,假设名称是MyDaemon,输入下

.NET跨平台实践:用C#开发Linux守护进程-Daemon

Linux守护进程(Daemon)是Linux的后台服务进程,它脱离了与控制终端的关联,直接由Linux init进程管理其生命周期,即使你关闭了控制台,daemon也能在后台正常工作. 一句话,为Linux开发与控制台无关的,需要在后台长时间不间断运行的“服务程序”,Daemon技术是非常重要的. Daemon程序一般用c/c++开发.不过,我今天要讲的,不是怎么用c/c++开发daemon,而是用C#! 一,创建Daemon程序: 用VS新建一个控制台项目,假设名称是MyDaemon,输入下

.NET跨平台实践:用C#开发Linux守护进程(转)

Linux守护进程(Daemon)是Linux的后台服务进程,它脱离了与控制终端的关联,直接由Linux init进程管理其生命周期,即使你关闭了控制台,daemon也能在后台正常工作. 一句话,为Linux开发与控制台无关的,需要在后台长时间不间断运行的“服务程序”,Daemon技术是非常重要的. Daemon程序一般用c/c++开发.不过,我今天要讲的,不是怎么用c/c++开发daemon,而是用C#! 一,创建Daemon程序: 用VS新建一个控制台项目,假设名称是MyDaemon,输入下

C++ Primer 学习笔记_44_STL实践与分析(18)--再谈迭代器【下】

STL实践与分析 --再谈迭代器[下] 三.反向迭代器[续:习题] //P355 习题11.19 int main() { vector<int> iVec; for (vector<int>::size_type index = 0; index != 10; ++index) { iVec.push_back(index); } for (vector<int>::reverse_iterator r_iter = iVec.rbegin(); r_iter !=

C++ Primer 学习笔记_43_STL实践与分析(17)--再谈迭代器【中】

STL实践与分析 --再谈迭代器[中] 二.iostream迭代[续] 3.ostream_iterator对象和ostream_iterator对象的使用 能够使用ostream_iterator对象将一个值序列写入流中,其操作过程与使用迭代器将一组值逐个赋值给容器中的元素同样: ostream_iterator<string> out_iter(cout,"\n"); istream_iterator<string> in_iter(cin),eof; wh

C++ Primer 学习笔记_42_STL实践与分析(16)–再谈迭代器【上】

STL实践与分析 --再谈迭代器[上] 引言: 另外三种迭代器类型: 1)插入迭代器:这类迭代器与容器绑定在一起,实现在容器中插入元素的功能. 2)iostream迭代器:这类迭代器可以与输入与输出流绑定在一起,用于迭代遍历所关联的IO流. 3)反向迭代器:这类迭代器实现向后遍历,而不是向前遍历,所有的容器都定义了自己的reverse_iterator类型,由rbegin和rend成员函数返回. 上述迭代器都在iterator头文件中定义. 一.插入迭代器 前面曾经提到的back_inserte

再谈 Go 语言在前端的应用前景

12 月 23 日,七牛云 CEO & ECUG 社区发起人许式伟先生在 ECUG Con 2018 现场为大家带来了主题为<再谈 Go 语言在前端的应用前景>的内容分享. 本文是对演讲内容的实录整理. 今年是举办 ECUG Con 的第 11 年,之前我谈的基本都是服务端的开发实践.从去年起我开始不谈后端而是谈前端.当然,去年我没有说为什么我会关注前端.今天再谈 Go 语言在前端的应用之前,我先简单聊一下思路脉络,为什么我今天会关注前端. 前端的演进 最早的 PC 时期,常见的设备主

浅谈敏捷软件开发与传统软件开发

本文将介绍传统软件开发与敏捷软件开发,并简单分析二者的优缺. 首先我查阅相关资料大致了解了下为什么会爆发"软件危机"和什么是"软件危机".由于在早期的软件开发活动中有明显的个体化特征,开发流程不规范,人们没有将软件与程序加以详细的区别,对程序之外的数据和相关文档资料没有给予重视,对编写程序之外的软件活动也没有给予重视,因此出现了"软件危机"."软件危机"的特点有:开发成本急剧上升.不能按时交付软件.软件难以维护.无法保证软件质