打开mc_att_control.h文件,我们可以看到主要由两个模块组成,一个是声明全局函数,另一个是声明一个类,如下所示:
extern "C" __EXPORT int mc_att_control_main(int argc, char *argv[]);
class MulticopterAttitudeControl : --- { --- };
声明全局函数的目的是告诉编译器,此处为mc_att_control模块的入口;
声明类的目的是为上述的全局函数提供服务。
我们跳到入口函数的实现部分,可以看到里面只有一行代码,即调用main()函数并返回“返回值”。
int mc_att_control_main(int argc, char *argv[])
{
return MulticopterAttitudeControl::main(argc, argv);
}
实际上MulticopterAttitudeControl这个类中并没有定义main()函数,但是它继承了基类ModuleBase,这个基类中定义了main()函数。
class MulticopterAttitudeControl :
public ModuleBase<MulticopterAttitudeControl>,
public ModuleParams,
public px4::WorkItem
{---};
紧接着我们跳到基类ModuleBase的main()函数实现部分,我们重点来看if (strcmp(argv[1], “start”) == 0)这部分,如果strcmp(argv[1], “start”) == 0,则说明传入的命令为start,执行start_command_base(argc - 1, argv + 1)函数,传入的命令数量减一,传入的命令指针加一。
static int main(int argc, char *argv[])
{
if (argc <= 1 ||
strcmp(argv[1], "-h") == 0 ||
strcmp(argv[1], "help") == 0 ||
strcmp(argv[1], "info") == 0 ||//这5种可能都说明在寻求帮助,那就打印帮助信息
strcmp(argv[1], "usage") == 0) {
return T::print_usage();
}
if (strcmp(argv[1], "start") == 0) {
// Pass the 'start' argument too, because later on px4_getopt() will ignore the first argument.
return start_command_base(argc - 1, argv + 1);
}
if (strcmp(argv[1], "status") == 0) {
return status_command();
}
if (strcmp(argv[1], "stop") == 0) {
return stop_command();
}
lock_module(); // Lock here, as the method could access _object.
int ret = T::custom_command(argc - 1, argv + 1);
unlock_module();
return ret;
}
如果我们是第一次执行start命令,在下面的start_command_base()函数中,我们调用T::task_spawn(argc, argv)函数,其中的T是参数,此时T代表的是MulticopterAttitudeControl,即调用MulticopterAttitudeControl::task_spawn(argc, argv)。
static int start_command_base(int argc, char *argv[])
{
int ret = 0;
lock_module();
if (is_running()) {
ret = -1;
PX4_ERR("Task already running");
} else {
ret = T::task_spawn(argc, argv);//系统第一次启动这个进程
if (ret < 0) {
PX4_ERR("Task start failed (%i)", ret);
}
}
unlock_module();
return ret;
}
在如下的task_spawn()函数中,我们的主要做三件事,一个是实例化一个类的对象,二是将这个模块给这对象初始化。要注意的是,实例化类的对象用了new关键字,说明是在堆区创建的实例,instance是类的指针,如果在堆区成功创建了MulticopterAttitudeControl类,则将类的地址储存在指针变量instance中,要明白,所谓的栈,堆只是人为划分并定义的一段内存而已,他们在物理层其实是一样的,比如在芯片中,栈、堆都只是ram中的某一个片段而已,在电脑硬件系统中,栈、堆都也只是内存条中的某一个片段而已,实际上内存条就是ram,我们常说的显卡中的显存也是ram。我们之所以将内存分区,是为了高效管理使用,实际的分区操作过程是操作系统完成的。
int MulticopterAttitudeControl::task_spawn(int argc, char *argv[])
{
bool vtol = false;
if (argc > 1) {
if (strcmp(argv[1], "vtol") == 0) {
vtol = true;
}
}
MulticopterAttitudeControl *instance = new MulticopterAttitudeControl(vtol);
if (instance) {
_object.store(instance);
_task_id = task_id_is_work_queue;
if (instance->init()) {
return PX4_OK; //说明成功
}
} else {
PX4_ERR("alloc failed");
}
delete instance;
_object.store(nullptr);
_task_id = -1;
return PX4_ERROR;
}