一个简单高效的 C++ 监控程序,带有一个通用的 Makefile

大家好!我是大聪明-PLUS
尽管语言模型已经发展成熟,但我认为我设计的监督程序版本或许足够有趣,值得写成一篇文章。该监督程序的作用是重启因某种原因崩溃并报错的程序。此外,如果程序在没有报错的情况下退出,则不会重启,也不会生成任何日志。日志会记录崩溃时间和错误类型。通用的 Makefile 文件可能很有用,因为它只需要放在源代码目录中,并添加必要的路径即可,例如:
LDFLAGS = -I/usr/include/boost
LIBS = -lboost_serialization

本文的主题并不新颖,但或许对某些人有用。文章主要关注后端,因为后端运行的连续性更为重要。值得注意的是,C++ 本身已经是一种相当健壮的编程语言。问题在于,教育机构通常先教授 C 语言,再教授 C++,而且 C++ 的编码风格往往是带有类的 C 语言。这自然导致了人们对 C++ 健壮性不足的印象。随着语言模型的出现,C++ 代码的健壮性已显著提高。我在生成的代码中没有遇到任何内存错误,逻辑错误虽然常见,但代码本身却堪称典范。
基础代码相对较小;我决定不添加过多的功能。主线程留空以便于自定义,被监控的程序则运行在单独的线程中。
直接主管代码

复制代码
#include <iostream>`
`#include <thread>`
`#include <atomic>`
`#include <unistd.h>`
`#include <sys/wait.h>`
`#include <fcntl.h>`
`#include <csignal>`
`#include <fstream>`
`#include <ctime>`
`#include <string>`
`#include <iomanip>`
`#include <sstream>`
`#include <condition_variable>`
`#include <mutex>`


`std::string` `getCurrentDateTime`() {
    `// `
    `std::time_t` `now` `=` `std::time`(`nullptr`);
    `std::tm*` `timeInfo` `=` `std::localtime`(`&now`);

    `// `
    `std::ostringstream` `oss`;
    `oss` `<<` `std::put_time`(`timeInfo`, `"%Y-%m-%d %H:%M:%S"`);

    `return` `oss`.`str`();  `// `
}

`std::ofstream` `createLogfile`() {
    `// `
    `std::time_t` `now` `=` `std::time`(`nullptr`);
    `std::tm*` `timeInfo` `=` `std::localtime`(`&now`);

    `// `
    `std::ostringstream` `oss`;
    `oss` `<<` `std::put_time`(`timeInfo`, `"%Y-%m-%d_%H-%M-%S"`);

    `std::string` `timestamp` `=` `oss`.`str`();
    `std::string` `filename` `=` `"logfile_"` `+` `timestamp` `+` `".txt"`;

    `// `
    `std::ofstream` `logFile`(`filename`);

    `if` (`logFile`.`is_open`()) {
    } `else` {
        `std::cerr` `<<` `""` `<<` `std::endl`;
    }

    `return` `logFile`; `// `
}

`int` `runApp`(`const` `std::string&` `program`, `int` `maxRestarts`, `std::atomic<bool>&` `shouldExit`, `std::condition_variable` `&cv`) {
    `int` `restartCount` `=` `0`;
	 `int` `status` `=` `0`;
		`std::ofstream` `log`;
		`bool` `log_is_created=false`;
    `while` (`restartCount` `<` `maxRestarts`) {
        `pid_t` `pid` `=` `fork`();
        `if` (`pid` `==` `0`) {
            `// `
            `execl`(`program`.`c_str`(), `program`.`c_str`(), `nullptr`);
            `perror`(`"execl"`);
            `exit`(`EXIT_FAILURE`);
        } `else` `if` (`pid` `<` `0`) {
            `perror`(`"fork"`);
            `exit`(`EXIT_FAILURE`);
        } `else` {
            `// `
            `waitpid`(`pid`, `&status`, `0`);
            `if` (`WIFEXITED`(`status`)) {
                `std::cerr` `<<` `"Program exited with status "` `<<` `WEXITSTATUS`(`status`) `<<` `std::endl`;
                `shouldExit` `=` `true`;
				`cv`.`notify_all`();
                `return` `0`;
            } `else` `if` (`WIFSIGNALED`(`status`)) {
            	`int` `sig=WTERMSIG`(`status`);
                `std::cerr` `<<` `"Program was killed by signal "` `<<` `sig` `<<` `std::endl`;
				 `switch` (`sig`) {
        `case` `SIGSEGV`:
            `std::cout` `<<` `"Segmentation fault"` `<<` `std::endl`;
            `break`;
        `case` `SIGABRT`:
            `std::cout` `<<` `"Aborted"` `<<` `std::endl`;
            `break`;
        `case` `SIGFPE`:
            `std::cout` `<<` `"Floating point exception"` `<<` `std::endl`;
            `break`;
        `case` `SIGILL`:
            `std::cout` `<<` `"Illegal instruction"` `<<` `std::endl`;
            `break`;
        `case` `SIGINT`:
            `std::cout` `<<` `"Interrupted by user (Ctrl+C)"` `<<` `std::endl`;
            `break`;
        `case` `SIGTERM`:
            `std::cout` `<<` `"Termination signal received"` `<<` `std::endl`;
            `break`;
        `default`:
            `std::cout` `<<` `"Unknown signal."` `<<` `std::endl`;
    	}
    	`if`(`log_is_created==false`) {`log` `=` `createLogfile`();`log_is_created=true`;}
	 	`if` (`log`.`is_open`())
	 	{
			`log<<getCurrentDateTime`();
			`switch` (`sig`) {
        `case` `SIGSEGV`:
            `log` `<<` `" Segmentation fault"` `<<` `std::endl`;
            `break`;
        `case` `SIGABRT`:
            `log` `<<` `" Aborted"` `<<` `std::endl`;
            `break`;
        `case` `SIGFPE`:
            `log` `<<` `" Floating point exception"` `<<` `std::endl`;
            `break`;
        `case` `SIGILL`:
            `log` `<<` `" Illegal instruction"` `<<` `std::endl`;
            `break`;
        `case` `SIGINT`:
            `log` `<<` `" Interrupted by user (Ctrl+C)"` `<<` `std::endl`;
            `break`;
        `case` `SIGTERM`:
            `log` `<<` `" Termination signal received"` `<<` `std::endl`;
            `break`;
        `default`:
            `log` `<<` `" Unknown signal."` `<<` `std::endl`;
    	}
			
		}
		`log`.`close`();
    }
        `restartCount++`;
        `std::cout` `<<` `"Restart count: "` `<<` `restartCount` `<<` `"/"` `<<` `maxRestarts` `<<` `std::endl`;
        }
    }

    `if` (`restartCount` `>=` `maxRestarts`) {
        `std::cerr` `<<` `"Max restarts reached. Exiting."` `<<` `std::endl`;
        `shouldExit` `=` `true`; `// `
		`cv`.`notify_all`();
    }
    `return` `0`;
}

`int` `main`(`int` `argc`, `char*` `argv`[]) {
    `if` (`argc` `!=` `3`) {
        `std::cerr` `<<` `"Usage: "` `<<` `argv`[`0`] `<<` `" <program> <max_restarts>"` `<<` `std::endl`;
        `return` `EXIT_FAILURE`;
    }

    `std::string` `program` `=` `argv`[`1`];
    `int` `maxRestarts` `=` `std::stoi`(`argv`[`2`]);

    `// `
    `std::atomic<bool>` `shouldExit`(`false`);
	`std::mutex` `mtx`;
	`std::condition_variable` `cv`;

    `try` {
        `std::thread` `appThread`(`runApp`, `program`, `maxRestarts`, `std::ref`(`shouldExit`), `std::ref`(`cv`));
        `appThread`.`detach`();
    } `catch` (`const` `std::system_error&` `e`) {
        `std::cerr` `<<` `"Failed to create thread: "` `<<` `e`.`what`() `<<` `std::endl`;
        `return` `EXIT_FAILURE`;
    }

    `// `
    `//`
    `//    `
    `//`
	`std::unique_lock<std::mutex>` `lock`(`mtx`);
    `while` (`!shouldExit`.`load`()) {
        `cv`.`wait`(`lock`);  `// `
    }

    `std::cout` `<<` `"Main thread exiting."` `<<` `std::endl`;
    `return` `0`;
}
`

接下来,我们来看通用(或部分通用)的 Makefile。它并非其他构建系统的直接竞争对手;它可以用于中小型项目、快速原型开发以及测试生成的 LLM 代码。例如,一个文件夹包含多个源文件(.cpp 和 .h 文件)。这可能是一个包含测试的小型项目。Makefile 会识别包含"int main"的文件。这些文件用于创建可执行文件。其余的 .cpp 文件用于创建目标文件,这些目标文件存储在 obj 文件夹中。是的,"int main"有可能出现在注释或字符串中。使用正则表达式是为了确保测试源文件可以编译在同一文件夹中;否则,将会出现 main 函数重复的错误。
Makefile 代码

复制代码
CXX` `=` `g++`
`CXXFLAGS` `=` `-Wall` `-Wextra` `-std=c++2a` `-O2` `-s` `-fdata-sections` `-ffunction-sections` `-flto`
`LDFLAGS` `=` `-I/usr/include/boost`
`LIBS` `=` `-lboost_serialization`

`# `
`SRCS` `=` `$`(`wildcard` `*`.`cpp`)

`# `
`MAIN_SRCS` `=` `$`(`shell` `grep` `-l` `"^\s*int\s\+main\s*"` `$`(`SRCS`))
`MAIN_EXES` `=` `$`(`MAIN_SRCS:`.`cpp=`)

`# `
`OTHER_SRCS` `=` `$`(`filter-out` `$`(`MAIN_SRCS`),`$`(`SRCS`))
`OTHER_OBJS` `=` `$`(`patsubst` `%`.`cpp`, `obj/%`.`o`, `$`(`OTHER_SRCS`))
`MAIN_OBJS` `=` `$`(`patsubst` `%`.`cpp`, `obj/%`.`o`, `$`(`MAIN_SRCS`))

`# `
`all:` `obj` `$`(`OTHER_OBJS`) `$`(`MAIN_OBJS`) `$`(`MAIN_EXES`)
	`@for` `dir` `in` `*/` ; `do` \
		`if` [ `"$$dir"` `!=` `"obj/"` ] `&&` [ `"$$dir"` `!=` `"*/"` ] `&&` [ `-f` `"$$dir/Makefile"` ]; `then` \
			`echo` `""`; \
			`$`(`MAKE`) `-C` `"$${dir%*/}"`; \
		`elif` [ `"$$dir"` `!=` `"obj/"` ] `&&` [ `"$$dir"` `!=` `"*/"` ]; `then` \
			`echo` `""`; \
		`fi`; \
	`done`

`# `
`obj:`
	`mkdir` `-p` `obj`

`# `
`obj/%`.`o:` `%`.`cpp`
	`$`(`CXX`) `$`(`CXXFLAGS`) `-MMD` `-MP` `-c` `-o` `$@` `$<`

`# `
`# `
`# `
`%:` `obj/%`.`o` `$`(`OTHER_OBJS`)
	`@echo` `"Linking $@"`
	`$`(`CXX`) `-o` `$@` `$<` `$`(`OTHER_OBJS`) `$`(`LDFLAGS`) `$`(`LIBS`)

`# `
`-include` `$`(`OTHER_OBJS:`.`o=`.`d`) `$`(`MAIN_OBJS:`.`o=`.`d`)

`# `
`clean:`
	`rm` `-rf` `obj` `$`(`MAIN_EXES`)
	`@echo` `""`
	`@for` `dir` `in` `*/` ; `do` \
		`if` [ `"$$dir"` `!=` `"obj/"` ] `&&` [ `"$$dir"` `!=` `"*/"` ] `&&` [ `-f` `"$$dir/Makefile"` ]; `then` \
			`echo` `""`; \
			`$`(`MAKE`) `-C` `"$${dir%*/}"` `clean`; \
		`elif` [ `"$$dir"` `!=` `"obj/"` ] `&&` [ `"$$dir"` `!=` `"*/"` ]; `then` \
			`echo` `""`; \
		`fi`; \
	`done`

.`PHONY:` `all` `clean`
`
相关推荐
生活很暖很治愈9 分钟前
Linux——基础IO&软硬链接
linux·ubuntu
2401_8589368822 分钟前
【Linux C 编程】标准 IO 详解与实战:从基础接口到文件操作实战
linux·c语言
Roc.Chang34 分钟前
Ubuntu 下 VLC 无法启动(Segmentation fault)终极解决方案
linux·ubuntu·vlc·媒体播放
松涛和鸣1 小时前
72、IMX6ULL驱动实战:设备树(DTS/DTB)+ GPIO子系统+Platform总线
linux·服务器·arm开发·数据库·单片机
简单中的复杂1 小时前
【避坑指南】RK3576 Linux SDK 编译:解决 Buildroot 卡死在 host-gcc-final 的终极方案
linux·嵌入式硬件
wVelpro2 小时前
如何在Pycharm 2025.3 版本实现虚拟环境“Make available to all projects”
linux·ide·pycharm
程序员老舅2 小时前
C++高并发精髓:无锁队列深度解析
linux·c++·内存管理·c/c++·原子操作·无锁队列
雨中风华3 小时前
Linux, macOS系统实现远程目录访问(等同于windows平台xFsRedir软件的目录重定向)
linux·windows·macos
爱吃生蚝的于勒3 小时前
【Linux】进程信号之捕捉(三)
linux·运维·服务器·c语言·数据结构·c++·学习
The森3 小时前
Linux IO 模型纵深解析 01:从 Unix 传统到 Linux 内核的 IO 第一性原理
linux·服务器·c语言·经验分享·笔记·unix