在Unix和其他多任务计算机操作系统中,后台程序是指,作为后台进程运行的计算机,而不是由交互用户直接控制。
后台程序概念的主要好处是,后台程序可以直接启动,而不需要将其发送到精确的用户或者shell的后台(然而,这不适用于Nuttx),其状态可以在运行的时候,通过shell查询。也可以终止。
Step 1: 创建一个小的标准应用
根据 FirstOnboard Application Tutorial (Hello Sky)教程(见PX4源码开发人员文档(二)),这是一个基本程序(简化):
[cpp] view plain copy .. __EXPORT int px4_daemon_app_main(int argc, char *argv[]); .. int px4_daemon_app_main(int argc, char *argv[]) { while (true) { warnx("Hello Daemon!\n"); sleep(1); } return 0; }
这个应用的问题非常明显,如果不使用&启动,将会阻塞shell(Nuttx,并不如此,并且会出于small footprint和可靠性的原因,支持CTRL-Z / fg / bg)。为了回避这个问题,下面部分将应用转换为一个后台程序。
Step 2: 创建后台进程管理函数
主函数由后台进程管理函数替代,旧的主函数的内容现在位于后台任务/进程中
[cpp] view plain copy #include <systemlib/systemlib.h> .. __EXPORT int px4_daemon_app_main(int argc, char *argv[]); .. int mavlink_thread_main(int argc, char *argv[]); .. int mavlink_thread_main(int argc, char *argv[]) { while (true) { warnx("Hello Daemon!\n"); sleep(1); if (thread_should_exit) break; } return 0; } .. int px4_daemon_app_main(int argc, char *argv[]) { if (argc < 1) usage("missing command"); if (!strcmp(argv[1], "start")) { if (thread_running) { warnx("daemon already running\n"); /* this is not an error */ exit(0); } thread_should_exit = false; daemon_task = task_spawn_cmd("daemon", SCHED_RR, SCHED_PRIORITY_DEFAULT, 4096, px4_daemon_thread_main, (argv) ? (const char **)&argv[2] : (const char **)NULL); thread_running = true; exit(0); } usage("unrecognized command"); exit(1); }
这将会启动一个新的任务,具有4096字节的堆栈,并传递非后台程序的具体指令行选项到后台主函数。典型的调用如下所示:
[plain] view plain copy px4_daemon_app start上面的代码没有报告状态,并且没有对多次调用后台进程进行保护。
Step 3: 添加停止/状态指令以及安全保护
具有合适的启动/停止/状态建立和附加安全保护的完整px4_daemon_app代码如下:
[cpp] view plain copy /** * @file px4_daemon_app.c * daemon application example for PX4 autopilot * * @author Example User <mail@example.com> */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <px4_config.h> #include <nuttx/sched.h> #include <systemlib/systemlib.h> #include <systemlib/err.h> static bool thread_should_exit = false; /**< daemon exit flag */ static bool thread_running = false; /**< daemon status flag */ static int daemon_task; /**< Handle of daemon task / thread */ /** * daemon management function. */ __EXPORT int px4_daemon_app_main(int argc, char *argv[]); /** * Mainloop of daemon. */ int px4_daemon_thread_main(int argc, char *argv[]); /** * Print the correct usage. */ static void usage(const char *reason); static void usage(const char *reason) { if (reason) { warnx("%s\n", reason); } warnx("usage: daemon {start|stop|status} [-p <additional params>]\n\n"); } /** * The daemon app only briefly exists to start * the background job. The stack size assigned in the * Makefile does only apply to this management task. * * The actual stack size should be set in the call * to task_create(). */ int px4_daemon_app_main(int argc, char *argv[]) { if (argc < 2) { usage("missing command"); return 1; } if (!strcmp(argv[1], "start")) { if (thread_running) { warnx("daemon already running\n"); /* this is not an error */ return 0; } thread_should_exit = false; daemon_task = px4_task_spawn_cmd("daemon", SCHED_DEFAULT, SCHED_PRIORITY_DEFAULT, 2000, px4_daemon_thread_main, (argv) ? (char *const *)&argv[2] : (char *const *)NULL); return 0; } if (!strcmp(argv[1], "stop")) { thread_should_exit = true; return 0; } if (!strcmp(argv[1], "status")) { if (thread_running) { warnx("\trunning\n"); } else { warnx("\tnot started\n"); } return 0; } usage("unrecognized command"); return 1; } int px4_daemon_thread_main(int argc, char *argv[]) { warnx("[daemon] starting\n"); thread_running = true; while (!thread_should_exit) { warnx("Hello daemon!\n"); sleep(10); } warnx("[daemon] exiting.\n"); thread_running = false; return 0; }
代码测试将会产生如下的输出:
[plain] view plain copy nsh> px4_daemon_app start [daemon] starting Hello Daemon!为了使用这一APP,只需在Firmware/makefiles/config_px4fmu_default.mk中,取消对这一示例部分的注释。