创建系统调用的编译器优化?

Posted

技术标签:

【中文标题】创建系统调用的编译器优化?【英文标题】:Compiler optimization creating a syscall? 【发布时间】:2014-05-13 18:59:40 【问题描述】:

我正在以两种模式编译一个相当复杂的应用程序:调试和发布。在我看来,主要区别在于 -O0 与 -O3 (如果需要,我可以提供 makefile 的相关部分)。我试图尽可能避免系统调用生成,因为我在系统调用仿真模式下模拟这个应用程序(没有操作系统在下面运行)。我目前遇到的问题是,在发布模式下,编译器会生成一个额外的套接字系统调用,我不想发生这种情况(在调试模式下也不会发生)。

我认为可能会创建套接字的原因是我正在使用 pthreads 并且我的两个线程正在通过 volatile char* 进行通信。所以我猜当我设置 -O3 标志时,编译器可能试图以一种奇特的方式实现它?但我不确定这是否是一个合理的假设。

    是否有可能因为 -O3 标志而生成了套接字系统调用? (没有太大意义) 如果是这样,我如何提示编译器避免生成此系统调用?

编辑:顺便说一句,代码是用 C 和 C++ 编写的

编辑:代码静态链接到以下共享库:

libstdc++.a 
libm.a 
libglib-2.0.a 
-static-libgcc 
*special pthreads library*

另外,我发现了在二进制文件中调用套接字的位置:

8c716:       db28            blt.n   8c76a <openlog_internal+0xf2>
8c718:       f8d9 1008       ldr.w   r1, [r9, #8]
8c71c:       4620            mov     r0, r4
8c71e:       2200            movs    r2, #0
8c720:       f441 2100       orr.w   r1, r1, #524288 ; 0x80000
8c724:       f001 e97c       blx     8da20 <__socket>
8c728:       4b20            ldr     r3, [pc, #128]  ; (8c7ac <openlog_internal+0x134>)
8c72a:       681b            ldr     r3, [r3, #0]
8c72c:       f8c9 0004       str.w   r0, [r9, #4]
8c730:       b943            cbnz    r3, 8c744 <openlog_internal+0xcc>
8c732:       1c43            adds    r3, r0, #1

编辑:我发现了为什么会发生这种情况(请参阅下面的答案)。如果有人解释为什么编译器会这样,请分享!!!

【问题讨论】:

发布编译器为其生成系统调用的代码。 除了发布代码之外,您可能还想通过比较为 -O0 与 -O3 生成的程序集来尝试自己解决这个问题。真的会生成系统调用吗? @CodyGray 感谢您的评论。我现在正在运行那个实验。为什么你认为这可能是有问题的?另外,你能建议一个简单的方法来做到这一点(我假设 objdump,但二进制文件是 2MB 和 5MB)。 @MichaelBurr 目前我不确定代码套接字是在哪里生成的,我会尽快发布确切位置。 -O3 可能是内联的东西。如果是这样,您可以在该函数上使用编译指示来停止它。 ...或在使用 -fno-inline 编译该对象时完全关闭内联 ...您可以将其切换为 -finiline-small-functions。 【参考方案1】:

虽然可以想象这样的优化,但我还没有听说过这样的优化,我真的很怀疑,因为任何系统调用通常都非常昂贵。

如果你在*nix系统上,你可以通过nm查找未定义的符号来验证它

nm -u file1.o file2.o | grep socket

应该在某处显示缺少的socket 符号为

        U socket

如果在某处调用了套接字。

正如我所提到的,我怀疑是否存在插入任何系统调用的优化,并且我希望上面的命令行没有输出。

更新:

在我的系统(Ubuntu 12.04,gcc 4.6)上,我在man gcc找到了以下注释

-O2 进一步优化。 ... 注意:在 Ubuntu 8.10 及更高版本中,默认设置 -D_FORTIFY_SOURCE=2, 并在 -O 设置为 2 或更高时激活。这使得额外的编译 - 几个 libc 函数的时间和运行时检查。要禁用,请指定 -U_FORTIFY_SOURCE 或 -D_FORTIFY_SOURCE=0。

所以,也许通过这种或类似的机制,当优化设置为-O2-O3 时包含一些代码。

【讨论】:

嗨奥拉夫。感谢您的答复。令人惊讶的是,这没有返回任何内容......这是否意味着正在从我链接的库中调用套接字? 如我所料。这意味着,没有对socket 的未定义引用(没有调用)。另请参阅我的更新答案。从你的评论来看,我想我给了一个错误的印象。很抱歉。 所以,我发现了系统调用的分支在二进制文件中发生的位置(请参阅更新的问题)。假设编译器仅在我执行 -O3 而不是在执行 -O0 时才在其中一个共享库中调用使用套接字的函数是否合理? 从汇编代码来看,我猜这是一些系统日志代码。你打电话给syslog() 还是openlog() 你在使用-O2时有同样的“优化”吗?【参考方案2】:

经过一整天的调试,结果是 arm-cross-gcc 在 -O0 和 -O1,-O2,-O3 下编译 strcpy() 的方式不同是一个volatile char*。 -O0 使用标准用户模式程序集进行编译,而 -O1,-O2,-O3 使用额外的系统调用进行编译,例如套接字、连接和发送。

所以,毕竟,我最初的预感是有道理的:

"我认为可能创建套接字的原因是我正在使用 pthreads 和我的两个线程正在通过 volatile 进行通信 字符*。所以我猜也许编译器正试图在 我设置 -O3 标志时的一种奇特方式?但我不确定这是否是 合理的假设。”

编辑:以下是支持这一说法的一些观察结果:

我将我的代码编译成 4 个版本

 1. without strcpy() -O0 => obj.o0
 2. with    strcpy() -O0 => obj_strcpy.o0
 3. without strcpy() -O3 => obj.o3
 4. with    strcpy() -O3 => obj_strcpy.o3

我对以上所有内容都运行了 nm -u。

这里是差异:

$> diff obj.o0 obj_strcpy.o0
$> diff obj.o3 obj_strcpy.o3
>          U __strcpy_chk
$>

这意味着当您将 strcpy() 添加到代码并使用 -O0 编译时,不会添加任何外部符号,而当您将 strcpy() 添加到代码并使用 -O3 编译时,会将 U __strcpy_chk 符号添加到对象文件。我将研究U __strcpy_chk 在 ARM 上的实现,以找出系统调用的来源。截至目前,U __strcpy_chk 似乎正在进行缓冲区溢出检查 - 这是参考:

http://refspecs.linux-foundation.org/LSB_4.0.0/LSB-Core-generic/LSB-Core-generic/libc---strcpy-chk-1.html

编辑:到目前为止,有两种解决方案:Olaf Dietsche 提出的一种解决方案是使用除 -O3 之外的另一个编译器标志。另一种选择是完全避免 strcpy() 并使用如下内容:

for(int i=0;i<64;i++)

  cmd[i] = msg[i];

【讨论】:

这(一个用套接字通信替换直接内存访问的优化器)似乎非常不太可能。 @AndrewMedico 我同意你的观点,当我用 -O0 或 -O3 编译 strcpy() 时,我看到了代码运行时的差异(-O3 版本调用套接字、连接和发送)。另外,请参阅我的答案中的编辑。如果您对此有更好的解释,请告诉我,因为我对答案也不完全满意。 @TayyarR 查看我更新的答案。这看起来像是一个额外的强化检查,在您的情况下包含在 -O3 中。您可以通过将 -U_FORTIFY_SOURCE 添加到编译器标志来禁用此功能。 因此,通过将您的答案与 Olaf 的答案放在一起,问题是 -O3(或 -O2 就此而言)导致 _FORTIFY_SOURCE 以调用 @987654333 的方式定义@ 编译为对 strcpy_chk() 的调用,其中显然包含(或导致具有)系统调用以记录有关缓冲区溢出问题的错误。 @MichaelBurr _FORTIFY_SOURCE 总是在一些系统上定义。在我和 @Tayyar 的系统上,_FORTIFY_SOURCE-O* 的组合在 /usr/include/features.h 中定义了 __USE_FORTIFY_LEVEL,这反过来又激活了一些额外的安全检查。

以上是关于创建系统调用的编译器优化?的主要内容,如果未能解决你的问题,请参考以下文章

RVO优化

编译器可以优化多个相同的函数调用吗

opaque函数调用在编译器优化中意味着什么?

编译器都做了哪些优化?

优化C++软件

优化C++软件