最近,我浏览了JOS Kernel的代码(在麻省理工学院开发,主要是为了帮助像我这样的初学者),并提出了一个小小的疑问,我认为这可能是微不足道的,但无法理解,所以在这里发帖寻求帮助。。
这是一段来自“.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; \
})
现在我有两个关于上述结构的问题-
我们不应该把
PADDR(nextfree)
的值赋给像var=PADDR(nextfree)
这样的变量,而不是像上面那样直接调用它吗怎么办?为什么有人更愿意在头文件中编写如此小而复杂的定义,而不是为指定的任务创建一个易于理解的函数。
最佳答案
当调用宏时,编译器会在此时将宏定义替换到代码中。不存在这样的“返回值”,除非宏碰巧扩展到具有返回值的对象。
此结构:
( { /* ... */ } )
是一个特定于gcc的扩展名,称为“语句表达式”,记录为here。它由括在圆括号中的复合语句组成,并生成最后一个表达式的值。(如果
; }
之前的最后一个东西不是表达式,则整个东西不会产生值。)PADDR()
宏接受一个内核虚拟地址kva
并生成相应的物理地址。如果虚拟地址无效,它将崩溃。(它本可以作为函数编写,但作者选择使用宏,可能是为了提高效率。inline
函数可能实现了相同的目标。)在您显示的代码中,使用
PADDR
:if (n > 0) {
/* snip */
PADDR(nextfree);
}
将调用
PADDR
宏,但它产生的值将被丢弃假设这不是一个错误,如果nextfree
不是有效的虚拟地址,那么这样做可能是为了强制死机。代码不使用产生的物理地址,因为它不需要它;检查就是它所需要的全部。它仍然会计算
__m_kva - KERNBASE;
,这可能有点浪费,但我怀疑成本是很高的——优化编译器可能会认识到结果没有被使用,并放弃计算。