- c - 在位数组中找到第一个零
- linux - Unix 显示有关匹配两种模式之一的文件的信息
- 正则表达式替换多个文件
- linux - 隐藏来自 xtrace 的命令
我正在阅读 Linux 内核源代码 (3.12.5 x86_64) 以了解如何处理进程描述符。
我发现要获取当前进程描述符我可以使用 current_thread_info() 函数,其实现如下:
static inline struct thread_info *current_thread_info(void)
{
struct thread_info *ti;
ti = (void *)(this_cpu_read_stable(kernel_stack) +
KERNEL_STACK_OFFSET - THREAD_SIZE);
return ti;
}
然后我查看了 this_cpu_read_stable()
:
#define this_cpu_read_stable(var) percpu_from_op("mov", var, "p" (&(var)))
#define percpu_from_op(op, var, constraint) \
({ \
typeof(var) pfo_ret__; \
switch (sizeof(var)) { \
...
case 8: \
asm(op "q "__percpu_arg(1)",%0" \
: "=r" (pfo_ret__) \
: constraint); \
break; \
default: __bad_percpu_size(); \
} \
pfo_ret__; \
})
#define __percpu_arg(x) __percpu_prefix "%P" #x
#ifdef CONFIG_SMP
#define __percpu_prefix "%%"__stringify(__percpu_seg)":"
#else
#define __percpu_prefix ""
#endif
#ifdef CONFIG_X86_64
#define __percpu_seg gs
#else
#define __percpu_seg fs
#endif
展开的宏应该是这样的内联汇编代码:
asm("movq %%gs:%P1,%0" : "=r" (pfo_ret__) : "p"(&(kernel_stack)));
根据 this post输入约束曾经是“m”(kernel_stack),这对我来说很有意义。但显然为了提高性能,Linus 将约束更改为“p”并传递了变量的地址:
It uses a "p" (&var) constraint instead of a "m" (var) one, to make gcc
think there is no actual "load" from memory. This obviously _only_ works
for percpu variables that are stable within a thread, but 'current' and
'kernel_stack' should be that way.
也在 post 中Tejun Heo 发表了以下评论:
Added the magical undocumented "P" modifier to UP __percpu_arg()
to force gcc to dereference the pointer value passed in via the
"p" input constraint. Without this, percpu_read_stable() returns
the address of the percpu variable. Also added comment explaining
the difference between percpu_read() and percpu_read_stable().
但是我结合修饰符“P”修饰符和约束“p(&var)”的实验没有奏效。如果没有指定段寄存器,“%P1”总是返回变量的地址。指针未取消引用。我必须使用括号来取消引用它,例如“(%P1)”。如果指定了段寄存器,没有括号 gcc 甚至不会编译。我的测试代码如下:
#include <stdio.h>
#define current(var) ({\
typeof(var) pfo_ret__;\
asm(\
"movq %%es:%P1, %0\n"\
: "=r"(pfo_ret__)\
: "p" (&(var))\
);\
pfo_ret__;\
})
int main () {
struct foo {
int field1;
int field2;
} a = {
.field1 = 100,
.field2 = 200,
};
struct foo *var = &a;
printf ("field1: %d\n", current(var)->field1);
printf ("field2: %d\n", current(var)->field2);
return 0;
}
我的代码有什么问题吗?或者我需要为 gcc 添加一些选项吗?此外,当我使用 gcc -S 生成汇编代码时,我没有看到通过使用“p”而不是“m”进行优化。非常感谢任何回答或评论。
最佳答案
您的示例代码不起作用的原因是因为 "p"
约束在内联汇编中的用途非常有限。所有内联汇编操作数都要求它们可以表示为汇编语言中的操作数。如果操作数不可表示,则编译器首先将其移动到寄存器并将其替换为操作数,从而使其成为可表示的。 "p"
约束有一个额外的限制:操作数必须是有效地址。问题是寄存器不是有效地址。寄存器可以包含地址,但寄存器本身不是有效地址。
这意味着 "p"
约束的操作数必须原样具有有效的程序集表示形式并且是有效的地址。您正在尝试使用堆栈上变量的地址作为操作数。虽然这是一个有效的地址,但它不是一个有效的操作数。堆栈变量本身有一个有效的表示(类似于 8(%rbp)
),但堆栈变量的地址没有。 (如果它是可表示的,它将类似于 8 + %rbp
,但这不是合法的操作数。)
在 "p"
约束下,您可以获取地址并用作操作数的少数事物之一是静态分配的变量。在这种情况下,它是一个有效的汇编操作数,因为它可以表示为立即值(例如,&kernel_stack
可以表示为 $kernel_stack
)。它也是一个有效地址,因此满足约束条件。
这就是为什么 Linux 内核宏可以工作而你的宏不能工作的原因。您试图将它与堆栈变量一起使用,而内核仅将它与静态分配的变量一起使用。
或者至少对于编译器来说看起来像是静态分配的变量。事实上,kernel_stack
实际上是分配在用于每个 CPU 数据的特殊部分中。该部分实际上并不存在,而是用作为每个 CPU 创建单独的内存区域的模板。此特殊部分中 kernel_stack
的偏移量用作每个 per CPU 数据区域中的偏移量,以存储每个 CPU 的单独内核堆栈值。 FS或GS段寄存器作为该区域的基址,每个CPU使用不同的地址作为基址。
这就是为什么 Linux 内核使用内联汇编来访问看起来像静态变量的东西。该宏用于将静态变量转换为每个 CPU 变量。如果您不想做这样的事情,那么从内核宏复制可能没有任何好处。您可能应该考虑采用不同的方式来完成您想要完成的任务。
现在,如果您在想,既然 Linus Torvalds 在内核中进行了这种优化,用 “p”
替换 “m”
约束,那么它一定是通常这样做是个好主意,您应该非常清楚这种优化是多么脆弱。它试图做的是让 GCC 认为对 kernel_stack
的引用实际上并没有访问内存,这样它就不会在每次更改内存时都重新加载该值。这里的危险是,如果 kernel_stack
确实改变了,那么编译器将被愚弄,并继续使用旧值。 Linus 知道每个 CPU 变量何时以及如何更改,因此可以确信宏在内核中用于其预期目的时是安全的。
如果您想消除自己代码中的冗余负载,我建议使用 -fstrict-aliasing
和/或 restrict
关键字。这样您就不会依赖于脆弱且不可移植的内联汇编宏。
关于c - gcc 内联汇编在 Linux 内核中使用修饰符 "P"和约束 "p"超过 "m",我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20965114/
我之前已经发布了一些这样的代码,试图在正确的位置获得侧边栏链接并以一种特殊的方式看起来,我决定朝着不同的方向前进。我现在需要的是知道我应该做什么来获得我在侧边栏旁边而不是下方标记为“内容”的 div。
我试图让多个 inline 和 inline-block 组件在 div 中垂直对齐。这个例子中的span怎么就非要往下推呢?我已经尝试了 vertical-align:middle; 和 verti
我试图让多个 inline 和 inline-block 组件在 div 中垂直对齐。这个例子中的span怎么就非要往下推呢?我已经尝试了 vertical-align:middle; 和 verti
我试图让多个 inline 和 inline-block 组件在 div 中垂直对齐。这个例子中的span怎么就非要往下推呢?我已经尝试了 vertical-align:middle; 和 verti
我很困惑...所以我在容器中有一个 UL,当我更改 UL 上方的 DIV 时,它似乎会影响 UL 之后的流程...发生了什么事? DIV 是 block 元素,对吗?和 UL 一样,对吧? 所以在这个
我问这个基本问题是为了澄清事实。都提到了这个问题及其目前接受的答案,这是不令人信服的。然而,投票第二多的答案提供了更好的洞察力,但也不是完美的。。在阅读下面的内容时,请尝试区分内联关键字和“内联”概念
function roll_over(img_name, img_src) { document[img_name].src = img_src; } 我使用此代码来显示 T 恤并在鼠标悬停时显
是否可以在 AngularJS 表达式的内联 if 语句中包含多个语句?例如,以下失败: ng-change="someCondition() ? doA(); doB() : doC()" ng-c
我在 RStudio 中使用 R Markdown 创建一个混合 Markdown 和 R 输出的报告。我知道如何在 Markdown 中使用内联 R 表达式,但我想知道如何进行相反的操作,即在 R
我们无法将表单标签添加到内联 CKEditor来自 chrome 和 IE,但它在 Firefox 中运行良好。如果我们将表单添加到内联 CKEditor,它会删除表单标签。 例如:如果我在 Fire
在我的 HTML 代码中,我有两个输入: Yes No 现在我有一个默认情况下的 div,因为它的样式显示内联,我希望当我单击“否”时,它会使其样式不显示,而"is"则会使其内联,所以我制作了此功
如何在 PowerShell 中创建带有内联 If 的语句(IIf,另请参阅: Immediate if 或 ternary If )? 如果您也认为这应该是 native PowerShell 函数
嗨。我在阅读以下问题后提出这个问题:Question_1和 Question_2 。 Question_1 没有任何合适的答案,Question_2 有替代解决方案,但不是完美的解决方案。 这里我有两
有人可以帮我解决以下语法或告诉我是否可行吗?因为我要修改 if ... else ... 条件。我不想在列表中添加重复的值,但我收到了 KeyError。 其实我不太熟悉这种说法: twins[val
有时我喜欢滥用 python 语法,特别是短的 if block : if True : print 'Hello' else : print 'Bye' 现在我尝试对函数定义做同样的事情: i
我在尝试将 Logo 容器底部对齐到零高度父级时遇到了问题。最好,我想用纯 CSS 来实现这一点。 在附带的 fiddle 中,我想让 control-group 的底部与零高度 panel 元素的顶
我需要内联编写 HTML 和 Javascript 代码,即在 HTML 正文中(需要显示一些随机整数值)我搜索了很多博客,但到目前为止没有找到任何帮助。请指教。 我想实现这个功能: Offer
好吧,这更多的是要求澄清 C++ 的一个特性是如何工作的,而不是一个是否可以的答案。我将从解释我遇到的问题开始,因为直接的答案是它不是一个很好的类设计。 我有一个类正在形成一个无法维护的 if 语句
我正在我的 C# 代码中获取一个数据库行。行包含 3 个不同的标志(3 列具有 true 或 false 值)。这些列中只有一列为真,这将决定该对象的类型。我如何在一行代码中确定该对象的类型。如果所有
在 CSS 中,我如何才能只将电话号码加粗,以便它与声明的其余部分内联,但电话号码是加粗的? 而不是在 HTML 中这样做: › Start posting jobs today– 0
我是一名优秀的程序员,十分优秀!