大家好!我是大聪明-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`
`