【问题标题】:Trying to understand the usage of function pointer试图理解函数指针的用法
【发布时间】:2014-06-27 02:14:32
【问题描述】:

这是u-boot's bootm.c 中启动内核的函数:

/* Subcommand: GO */
static void boot_jump_linux(bootm_headers_t *images, int flag)
{
#ifdef CONFIG_ARM64
void (*kernel_entry)(void *fdt_addr);
int fake = (flag & BOOTM_STATE_OS_FAKE_GO);

kernel_entry = (void (*)(void *fdt_addr))images->ep;

debug("## Transferring control to Linux (at address %lx)...\n",
(ulong) kernel_entry);
bootstage_mark(BOOTSTAGE_ID_RUN_OS);

announce_and_cleanup(fake);

if (!fake)
kernel_entry(images->ft_addr);
#else
unsigned long machid = gd->bd->bi_arch_number;
char *s;
void (*kernel_entry)(int zero, int arch, uint params);
unsigned long r2;
int fake = (flag & BOOTM_STATE_OS_FAKE_GO);

kernel_entry = (void (*)(int, int, uint))images->ep;

s = getenv("machid");
if (s) {
strict_strtoul(s, 16, &machid);
printf("Using machid 0x%lx from environment\n", machid);
}

debug("## Transferring control to Linux (at address %08lx)" \
"...\n", (ulong) kernel_entry);
bootstage_mark(BOOTSTAGE_ID_RUN_OS);
announce_and_cleanup(fake);

if (IMAGE_ENABLE_OF_LIBFDT && images->ft_len)
r2 = (unsigned long)images->ft_addr;
else
r2 = gd->bd->bi_boot_params;

if (!fake)
kernel_entry(0, machid, r2);
#endif
}

我很难理解kernel_entry 是如何在这里工作的。特别是在倒数第二行中,它被用作:

   kernel_entry(0, machid, r2);

那么kernel_entry()的定义在哪里?我在整个 u-boot 和内核源代码中找不到。

更新

我在这里重新表述我的问题:

假设kernel_entry 是一个指向函数的指针,并被定义为:

bootm_headers_t *images
kernel_entry = (void (*)(int, int, uint))images->ep;

然后在程序的某个地方被称为:

 kernel_entry(0, machid, r2);

我理解作为一个指针,kernel_entry 应该存储一个函数的地址。但我想了解对这三个参数执行哪些操作。为什么我们会有这些论点?

【问题讨论】:

  • kernel_entry = (void (*)(int, int, uint))images->ep;
  • 函数会对这三个参数(即 int、int、uint)执行什么操作?
  • 它不是一个函数,它是一个指针转换,因此您可以使用 3 个参数调用 kernel_entry,前两个参数为 int,第三个参数为 uint
  • 谢谢。你能解释一下'kernel_entry = (void (*)(int, int, uint))images->ep;'中到底发生了什么吗?并作为答案回复?
  • 您介意格式化代码以使其可读吗?

标签: c linux u-boot


【解决方案1】:

kernel_entry 变量及其类型的声明,它是一个指向函数的指针,该函数采用intintuint 并返回void(可能是最令人困惑的部分),在这里:

void (*kernel_entry)(int zero, int arch, uint params);

赋值,images->ep 被转换成想要的签名函数指针并放入变量中:

kernel_entry = (void (*)(int, int, uint))images->ep;

最后调用函数:

kernel_entry(0, machid, r2);

请注意,如果定义了CONFIG_ARM64,那么kernel_entry指向的函数有不同的签名:

void (*kernel_entry)(void *fdt_addr); //takes one void* param and returns void

【讨论】:

  • kernel_entry 是指向“函数”的指针。那个功能在哪里?它必须有一个定义,显示如何处理这三个参数...
  • 该功能是通过images->ep提供的。它是在其他地方定义的,早于boot_jump_linux 被调用。
  • 我也找不到任何关于 ep 的信息。它应该是一个结构?
  • 函数定义无关。正如您可以将*int 转换为*float 一样,您可以将函数指针转换为其他任何东西。很明显,这段代码的作者把类型检查扔到了窗外。
  • 这就是指针的重点:不再使用该名称。在这里,我为你做了一个例子:codepad.org/j5xnuy1N
【解决方案2】:

U-Boot 在其可寻址内存空间中拥有内核映像,读取该映像中包含的地址(在 images->ep 处),然后分支到该入口点地址。

“kernel_entry() 的定义”实际上在内核源代码中,arch/arm/boot/compressed/head.S 中的标签“start”就是您要查找的内容。

要了解内核启动过程,IMO 权威教程是 Hallinan “Embedded Linux Primer”的第 5 章。

【讨论】:

  • 感谢您的信息。但实际上任何地方都没有这样的文件。你能交叉检查一下文件名/位置吗?
  • 我还找到了另一个.S文件:arch/arm/kernel/head-common.S,它有thsi标签。
  • 非常抱歉,我的错字,它是arch/arm/boot/compressed/head.S。这个“开始”标签对于我的 Cortex-A9 目标上的 v3.14.3 绝对正确。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-06-28
  • 2013-10-08
  • 2013-01-18
  • 2015-01-28
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多