一个 Linux 后台程序编程案例分析

2021/11/28 7:13:24

本文主要是介绍一个 Linux 后台程序编程案例分析,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

Linux 下的一个进程打开一个日志文件,不定期地往该文件里写入日志。此时可以在控制台使用 mv 命令给该日志文件改个名字或者用 rm 命令把这个日志文件删除掉。Linux 下是允许这么干的!对于改日志文件名的情形还好一点,后续的日志还是会写入更名后的文件里,只是会影响后面日志文件自动清理功能(比如把日志文件名改得像个进程文件名);而对于删除文件的情形,就直接导致后续的日志无法计入日志文件,一直到第二天凌晨日志文件切换时才回复正常。

为此,增加了一个日志文件保护功能,放在一个独立的线程 logGuardEntry 里运行。这个保护功能主要是定期检查当前所使用的日志文件是否存在,以及日志记录是否正常,若检测到异常,则关闭当前日志输出的文件句柄,并重新打开所使用的文件,文件不存在则重建。

用 debug 版调试运行,功能正常后生成 release 版,却测试发现增加的日志文件保护功能没有起作用。用 gdb 挂上进程查看线程栈,想看下 logGuardEntry 线程内部出了什么状况,结果发现根本就没有 logGuardEntry 这个线程!

仔细排查,才发现问题和 daemonInit 函数调用有关系。main 函数里相关调用示意如下:

int main()
{
    ...
    startLogWriter(...);
    ...
#ifndef _DEBUG
    daemonInit();
#endif
    ...
}

startLogWriter 函数体末尾有一行:

startThread(logGuradEntry,...);

 即启动一个以 logGuardEntry 为入口函数的线程,实施日志文件保护的功能。

daemonInit 函数里有如下代码段:

void daemonInit()
{
    ...
    pid = fork();
    if (pid != 0) {
        exit(0);
    }
    ...
}

这是让程序以守护进程运行的通常做法,即让主进程退出,而让子进程经过进一步处理成为守护进程继续运行。但是 fork 调用生成的子进程在 fork() 这条语句完成时,只会有一个线程,即调用 fork 的线程(上面就是 main 所在的线程,即主线程)。这是 Linux 出于某种合理的考虑而这样设计的。上面的 main 函数里,先调用了 startLogWriter,里面会启动一个 logGuard 线程,这时主进程有两个线程在运行;而随后调用 daemonInit,导致主进程退出,而子进程却丢掉了 logGuard 线程,导致测试时发现日志保护功能根本不起作用。

当然,这个问题改起来很简单,把 startLogWriter 调用放到 daemonInit 之后就好了,即:

int main()
{
    ...
#ifndef _DEBUG
    daemonInit();
#endif
    startLogWriter(...);
    ...
}

就是说,对于以守护进程运行的后台程序而言,daemonInit 调用尽量早一些做,尤其不要在调用 daemonInit 之前启动工作线程。

由 fork 调用的工作机制,不禁会想:子进程是不是可以没有 main() 函数所在的线程,即所谓主线程(比如,把上面的 daemonInit 调用挪到 logGuard 线程里调用)?

实际试验了一下,果然是可以的。以下是用 gdb 挂上进程看到的内情:

 



这篇关于一个 Linux 后台程序编程案例分析的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!


扫一扫关注最新编程教程