- 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/
总的来说,我对 Linux 内核和操作系统非常感兴趣。我想知道的是,内核的文件类型或扩展名是什么?它显然没有 .exe 或 .out 扩展名,因为它们用于安装在操作系统上的应用程序。 内核只是一个二进
我需要为 Raspbian Linux 内核添加一个自己的系统调用。现在我在搜索了大约 2 天以找到解决方案后陷入困境。 要加一个系统调用,我基本上是按照大纲来的( http://elinux.org
对于一个学术项目,我希望将源文件 (myfile.c) 添加到 kernel/目录,与exit.c相同的目录和 fork.c .构建系统似乎不会自动获取新文件,因为我在 myfile.c 中定义的函数
浏览器排行榜 浏览器市占率排行榜全球榜 。 浏览器市占率排行榜中国榜 -快科技 。 如果按照浏览器内核来看, Chromium 内核的市场占有率无疑是最大的,一家独大
给定一个进程或线程的任务结构,迭代属于同一进程的所有其他线程的习惯用法是什么? 最佳答案 Linux 不区分进程(任务)和线程。库调用 fork() 和 pthread_create() 使用相同的系
我正在用c(不是linux。完全从头开始)从头开始制作一个内核,但我遇到了一些问题。我有这个代码: #include "timer.h" int ms = 0; void timer_handler(
我正在从头开始制作一个 C 内核,我实际上只是从网站上复制了这段代码,因为我的代码无法工作,所以我很困惑。 void kmain(void) { const char *str = "my f
我不确定,如果我完全理解上述差异,所以我想自己解释一下,你可以打断我,只要我有错:“内核是创建内核线程的初始代码段。内核线程是由内核管理的进程。用户线程是进程的一部分。如果你有一个单线程进程,那么整个
看一下struct file 定义from this code Linux 内核版本 2.6.18。 我正在尝试比较代码中的两个 struct file 变量,并确定它们是否指的是同一个文件。该结构中
我试图在 Linux 启动时使嵌入式设备中的 LED 闪烁。基本上,LED 闪烁表明 Linux 正在启动。为了使 LED 闪烁,我正在做以下事情 在 init/main.c 中创建了一个全局定时器(
我有一些在 FreeBSD 和 Linux 上运行的特定硬件。 我必须做一个用户空间应用程序,它将使用内核/用户空间应用程序之间的共享内存与驱动程序一起工作。我的应用程序对来自用户空间的共享内存进行忙
我在哪里可以找到 linux 内核中相应函数的解释,特别是对于 ICMPv4? 例如:icmp_reply、icmp_send等 感谢您的帮助。 最好的,阿里木 最佳答案 探索 Linux 内核中的
我在 Linux Kernel 3.4 上工作,我有以下代码: /* Proximity sensor calibration values */ unsigned int als_kadc;
我正在阅读“罗伯特·洛夫 (Robert Love) 撰写的 Linux 内核开发第 3 版”,以大致了解 Linux 内核的工作原理..(2.6.2.3) 我对等待队列的工作方式感到困惑,例如这段代
我之前也问过同样的问题,但是我的帖子不知为何被删除了。 无论如何,我正在尝试使用 C++ 并编写一个允许我直接访问内存并向其中写入内容的程序。我听说我需要对内核做一些事情,因为它是连接操作系统和应用程
在尝试了解 Ruby 执行方法时,我找到了这篇关于在 Ruby 中运行命令的五种方法的博文 http://mentalized.net/journal/2010/03/08/5_ways_to_run
是否有 Linux 发行版(Minix 除外)包含良好的源代码文档?或者,是否有一些好的文档来描述一般的 Linux 源代码? 我已经下载了内核源代码,但是(不出所料)我有点不知所措,我想知道是否有一
有谁知道 linux 中的哪个函数或文件包含查找用于 bind() 系统调用的随机端口的算法?我到处寻找,在 Linux 源代码中找不到包含此算法的方法。 谢谢! 最佳答案 这是一段又长又复杂的代码,
前言 首先,对于有科班背景的读者,可以跳过本系列文章。这些文章的主要目的是通过简单易懂的汇总,帮助非科班出身的读者理解底层知识,进一步了解为什么在面试中会涉及这些底层问题。否则,某些概念将始终
CentOS7.2与CentOS6区别及特点 Linux 操作系统的启动首先从 BIOS 开始,接下来进入 boot loader,由 bootloader 载入内核,进行内核初始化。内核初始化的
我是一名优秀的程序员,十分优秀!