.NET跨平台实践:再谈用C#开发Linux守护进程 — 完整篇
Linux守護(hù)進(jìn)程是Linux的后臺(tái)服務(wù)進(jìn)程,相當(dāng)于Windows服務(wù),對(duì)于為L(zhǎng)inux開(kāi)發(fā)服務(wù)程序的朋友來(lái)說(shuō),Linux守護(hù)進(jìn)程相關(guān)技術(shù)是必不可少的,因?yàn)檫@個(gè)技術(shù)不僅僅是為了開(kāi)發(fā)守護(hù)進(jìn)程,還可以拓展到多進(jìn)程,父子進(jìn)程文件描述符共享,父子進(jìn)程通訊、控制等方面,是實(shí)現(xiàn)Linux大型服務(wù)的基礎(chǔ)技術(shù)之一。
去年我也曾寫(xiě)了一篇關(guān)于守護(hù)進(jìn)程的帖子,名字叫《.NET跨平臺(tái)實(shí)踐:用C#開(kāi)發(fā)Linux守護(hù)進(jìn)程》,這篇文章的的確確實(shí)現(xiàn)了一個(gè)Daemon,不過(guò),它有一個(gè)弱點(diǎn),不能運(yùn)行多線(xiàn)程!
這篇帖子的目的就是進(jìn)一步完善,讓我們寫(xiě)出一個(gè)功能完整,可以用于生產(chǎn)環(huán)節(jié)的基本的守護(hù)進(jìn)程。
先帖代碼(假設(shè)項(xiàng)目名是daemon):
using System;
using System.Threading;
using System.Timers;
using System.Runtime.InteropServices;
using System.IO;
using System.Text;
/********************************************
?* 一個(gè)完整的linux daemon示例,作者宇內(nèi)流云 *
?********************************************/
namespace daemon
{
? ? class Program
? ? {
? ? ? ? const string DaemonTag = "--daemon.";
? ? ? ? static void Main(string[] args)
? ? ? ? {
? ? ? ? ? ? // 判斷是否已經(jīng)進(jìn)入Daemon狀態(tài),如果是,就直接執(zhí)行后臺(tái)主函數(shù)
? ? ? ? ? ? if (string.IsNullOrEmpty(Environment.GetEnvironmentVariable(DaemonTag)) == false)
? ? ? ? ? ? {
? ? ? ? ? ? ? ? Environment.SetEnvironmentVariable(DaemonTag, null);
? ? ? ? ? ? ? ? DaemonMain(args);
? ? ? ? ? ? ? ? return;
? ? ? ? ? ? }
? ? ? ? ? ? // 如果還沒(méi)有進(jìn)入daemon狀態(tài),就作daemon處理
? ? ? ? ? ? /
? ? ? ? ? ? int pid = fork();
? ? ? ? ? ? if (pid != 0) exit(0);
? ? ? ? ? ? setsid();
? ? ? ? ? ? pid = fork();
? ? ? ? ? ? if (pid != 0) exit(0);
? ? ? ? ? ? umask(0);
? ? ? ? ? ? // 這兒已經(jīng)進(jìn)入“守護(hù)進(jìn)程”工作狀態(tài)了!
? ? ? ? ? ? // 關(guān)閉所有打開(kāi)的文件描述符
? ? ? ? ? ? int max = open("/dev/null", 0);
? ? ? ? ? ? for (var i = 0; i <= max; i++) { close(i); }
? ? ? ? ? ? // 設(shè)置標(biāo)記,防止重復(fù)運(yùn)行進(jìn)入
? ? ? ? ? ? Environment.SetEnvironmentVariable(DaemonTag,"yes");
? ? ? ? ? ? //為execp參數(shù)重組參數(shù)
? ? ? ? ? ? var args1 = args == null ? new string[2] : new string[args.Length + 2];
? ? ? ? ? ? args1[0] = "MyDaemon";
? ? ? ? ? ? args1[1] = Path.Combine(Environment.CurrentDirectory, Thread.GetDomain().FriendlyName);
? ? ? ? ? ? if (args1.Length > 2)
? ? ? ? ? ? {
? ? ? ? ? ? ? ? for (var i = 0; i < args.Length; i++)
? ? ? ? ? ? ? ? { args1[i + 2] = args[i]; }
? ? ? ? ? ? }
? ? ? ? ? ? //守護(hù)狀態(tài)下重新加載和運(yùn)行本程序
? ? ? ? ? ? execvp("mono", args1);
? ? ? ? }
? ? ? ? /// <summary>
? ? ? ? /// Daemon工作狀態(tài)的主方法
? ? ? ? /// </summary>
? ? ? ? /// <param name="aargs"></param>
? ? ? ? static void DaemonMain(string[] aargs)
? ? ? ? {
? ? ? ? ? ? //啟動(dòng)一個(gè)線(xiàn)程去處理一些事情
? ? ? ? ? ? (new Thread(DaemonWorkFunct) { IsBackground = true }).Start();
? ? ? ? ? ? //daemon時(shí),控制臺(tái)輸入、輸出流已經(jīng)關(guān)閉
? ? ? ? ? ? //請(qǐng)不要再用Console.Write/Read等方法
? ? ? ? ? ? //阻止daemon進(jìn)程退出
? ? ? ? ? ? (new AutoResetEvent(false)).WaitOne();
? ? ? ? }
? ? ? ? static FileStream fs;
? ? ? ? static int count = 0;
? ? ? ? static void DaemonWorkFunct() {
? ? ? ? ? ? fs = File.Open("/tmp/daemon.txt", FileMode.OpenOrCreate);
? ? ? ? ? ? var t = new System.Timers.Timer() { Interval = 1000 };
? ? ? ? ? ? t.Elapsed += OnElapsed;
? ? ? ? ? ? t.Start();
? ? ? ? }
? ? ? ? private static void OnElapsed(object sender, ElapsedEventArgs e)
? ? ? ? {
? ? ? ? ? ? var s = DateTime.Now.ToString("yyy-MM-dd HH:mm:ss") + "\n";
? ? ? ? ? ? var b = Encoding.ASCII.GetBytes(s);
? ? ? ? ? ? fs.Write(b, 0, b.Length);
? ? ? ? ? ? fs.Flush();
? ? ? ? ? ? count++;
? ? ? ? ? ? if (count > 100) {
? ? ? ? ? ? ? ? fs.Close();
? ? ? ? ? ? ? ? fs.Dispose();
? ? ? ? ? ? ? ? exit(0);
? ? ? ? ? ? }
? ? ? ? }
? ? ? ? [DllImport("libc", SetLastError = true)]
? ? ? ? static extern int fork();
? ? ? ? [DllImport("libc", SetLastError = true)]
? ? ? ? static extern int setsid();
? ? ? ? [DllImport("libc", SetLastError = true)]
? ? ? ? static extern int umask(int mask);
? ? ? ? [DllImport("libc", SetLastError = true)]
? ? ? ? static extern int open([MarshalAs(UnmanagedType.LPStr)]string pathname, int flags);
? ? ? ? [DllImport("libc", SetLastError = true)]
? ? ? ? static extern int close(int fd);
? ? ? ? [DllImport("libc", SetLastError = true)]
? ? ? ? static extern int exit(int code);
? ? ? ? [DllImport("libc", SetLastError = true)]
? ? ? ? static extern int execvp([MarshalAs(UnmanagedType.LPStr)]string file, string[] argv);
? ? }
}
以上代碼的工作過(guò)程是:判斷程序自身是否已經(jīng)處于daemon(后臺(tái)服務(wù))狀態(tài),如果是,就直接開(kāi)始具體的服務(wù)工作(開(kāi)啟一個(gè)線(xiàn)程,每秒向 /tmp/daemon.txt中打印一行字符,100次后退出),如果不是daemon狀態(tài),就進(jìn)入Daemon處理,使之進(jìn)入daemon工作狀態(tài)。
以上代碼編譯后,會(huì)生成一個(gè)叫 daemon.exe 的程序,當(dāng)然,這個(gè)程序是為linux開(kāi)發(fā)的,不能在windows上運(yùn)行?,F(xiàn)在,我把它放到linux上面,用mono daemon.exe命令啟動(dòng)它。
這時(shí)我們可以看到這個(gè)程序啟動(dòng)后,控制臺(tái)上沒(méi)有任何輸出,也沒(méi)有阻塞控制臺(tái),那么,在哪兒能找到它呢?用 ps -ef命令看看,原來(lái)它真的已經(jīng)在后臺(tái)運(yùn)行起來(lái)了。
再看看這個(gè)后臺(tái)進(jìn)程是否完成了它的工作:cat /tmp/daemon.txt 查看文件內(nèi)容:
從生成的文件內(nèi)容看,這個(gè)Daemon服務(wù)程序的確按我們的設(shè)計(jì)意圖,每秒鐘向/tmp/daemon.txt打印了一行字符。
原文地址:http://www.cnblogs.com/yunei/p/6761218.html
.NET社區(qū)新聞,深度好文,微信中搜索dotNET跨平臺(tái)或掃描二維碼關(guān)注
總結(jié)
以上是生活随笔為你收集整理的.NET跨平台实践:再谈用C#开发Linux守护进程 — 完整篇的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 监控——《微服务设计》读书笔记
- 下一篇: 未来的C#之只读引用与结构体