1

最近,我正在查看 JOS Kernel 的代码(在 MIT 开发,主要是为了帮助像我这样的初学者)并提出了一个小疑问,我认为这可能是微不足道的,但无法弄清楚,所以在这里发帖寻求帮助。 .

这是来自“.c”文件的一小段代码:-

if(n>0)
           {
            nextfree = ROUNDUP((char *) nextfree, PGSIZE);
            result=nextfree;
            nextfree+=n;
            PADDR(nextfree);
           }

对应的“.h”文件:-

/* This macro takes a kernel virtual address -- an address that points above
* KERNBASE, where the machine's maximum 256MB of physical memory is mapped --
* and returns the corresponding physical address.  It panics if you pass it a
* non-kernel virtual address.
*/

    #define PADDR(kva)                      \
    ({                              \
    physaddr_t __m_kva = (physaddr_t) (kva);        \
    if (__m_kva < KERNBASE)                 \
    panic("PADDR called with invalid kva %08lx", __m_kva);\
    __m_kva - KERNBASE;                 \
    })

现在我对上述结构有两个问题-

  1. 我们不应该PADDR(nextfree)为某个变量赋值,var=PADDR(nextfree)而不是像上面那样直接调用它。它将如何运作?

  2. 为什么有人更愿意在头文件中编写如此小而复杂的定义,而不是为指定任务创建一个易于掌握的函数。

4

2 回答 2

4

当您调用宏时,编译器会在此时将宏定义替换到您的代码中。没有这样的“返回值”,除非宏恰好扩展为具有返回值的东西。

这个构造:

 ( { /* ... */ } )

是一个特定于 gcc 的扩展,称为“语句表达式”,在此处记录。它由括在括号中的复合语句组成,并产生最后一个表达式的值。(如果 之前的最后一件事; }不是表达式,那么整个事情不会产生值。)

PADDR()宏采用内核虚拟地址kva并产生相应的物理地址。如果虚拟地址无效,它会恐慌。(它可以写成一个函数,但作者选择使用宏,可能是为了提高效率。一个inline函数可能也能达到同样的目的。)

在您展示的代码中,使用PADDR

if (n > 0) {
    /* snip */
    PADDR(nextfree);
}

PADDR调用宏,但它产生的值被丢弃。假设这不是错误,如果nextfree不是有效的虚拟地址,这可能是为了强制恐慌。代码不使用生成的物理地址,因为它不需要它;检查就是它所需要的。

它仍然计算__m_kva - KERNBASE;,这可能有点浪费,但我怀疑成本是否很大——优化编译器可能会识别出结果未使用,并丢弃计算。

于 2013-10-15T23:57:40.067 回答
1

Well... unpicking this:

  1. Notice the parens. This means whatever is inside it will reduce to a single value...
  2. Notice the braces: we're defining a new block scope here.
  3. Now we're in the new scope, we can safely define whatever variables we like because they won't escape this block.
  4. The last statement consists of an expression.
  5. Therefore the value of the scope is the value of that expression in the last statement.
  6. Therefore the return value of the macro is __m_kva - KERNBASE.

So to answer question 1: it does return a value, but in your example snippet this returned value is simply not used. Presumably other code that calls the macro does use that value, though. To answer question 2: that depends. You can do things with macro's which you simply cannot do with functions, like undefining them. In this case it seems the programmer needed to replicate common error-checking, and used the translation macro to do it in the sample snippet. (It checks in advance that the next free will not inadvertently attempt to free userspace memory or something like that.)

于 2013-10-15T23:43:22.343 回答