Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/143.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ C/C++;不带程序集的函数定义_C++_Inline Assembly - Fatal编程技术网

C++ C/C++;不带程序集的函数定义

C++ C/C++;不带程序集的函数定义,c++,inline-assembly,C++,Inline Assembly,我一直认为像printf()这样的函数在最后一步中是使用内联汇编定义的。在stdio.h的内心深处埋藏着一些asm代码,这些代码实际上告诉CPU该做什么。例如,在dos中,我记得它是通过首先mov将字符串的开头放入某个内存位置或寄存器,然后调用intterupt来实现的 然而,由于visualstudio的x64版本根本不支持内联汇编程序,这让我想知道在C/C++中怎么可能没有汇编程序定义的函数。像printf()这样的库函数如何在不使用汇编代码的情况下在C/C++中实现?什么实际执行正确的软件

我一直认为像
printf()
这样的函数在最后一步中是使用内联汇编定义的。在stdio.h的内心深处埋藏着一些asm代码,这些代码实际上告诉CPU该做什么。例如,在dos中,我记得它是通过首先
mov
将字符串的开头放入某个内存位置或寄存器,然后调用
int
terupt来实现的


然而,由于visualstudio的x64版本根本不支持内联汇编程序,这让我想知道在C/C++中怎么可能没有汇编程序定义的函数。像
printf()
这样的库函数如何在不使用汇编代码的情况下在C/C++中实现?什么实际执行正确的软件中断?谢谢。

编译器从C/C++源代码生成程序集。

标准库函数在底层平台库(例如UNIX API)和/或直接系统调用(仍然是C函数)上实现。系统调用(在我所知的平台上)是通过调用具有内联asm的函数在内部实现的,该函数将系统调用号和参数放入CPU寄存器,并触发内核随后处理的中断


除了系统调用之外,还有其他与硬件通信的方式,但在现代操作系统下运行时,这些方式通常不可用或相当有限,或者至少启用它们需要一些系统调用。一个设备可以是内存映射的,因此对特定内存地址的写入(通过常规指针)可以控制该设备。I/O端口也经常被使用,并且根据体系结构,这些特殊的CPU操作码访问它们,或者它们也可以是映射到特定地址的内存。

< P>,除分号和注释之外的所有C++语句最终成为机器代码,告诉CPU要做什么。您可以编写自己的printf函数,而无需借助汇编。必须在程序集中写入的唯一操作是端口的输入和输出,以及启用和禁用中断的操作


然而,出于性能原因,汇编仍然在系统级编程中使用。即使不支持内联程序集,也不会阻止您在程序集中编写单独的模块并将其链接到应用程序。

通常,库函数是预编译的,并分发ad对象。内联汇编程序仅在特定情况下出于性能原因使用,但这是例外,而不是规则。事实上,printf在我看来并不是一个很好的内联组装的候选者。Insetad是memcpy或memcmp之类的函数。非常低级的函数可能由本机汇编程序(masm?gnu asm?)编译,并作为库中的对象分发。

首先,您必须理解环的概念。
内核在环0中运行,这意味着它可以完全访问内存和操作码。
程序通常在环3中运行。它对内存的访问有限,无法使用所有操作码。

因此,当软件需要更多权限(打开文件、写入文件、分配内存等)时,它需要询问内核。
这可以通过多种方式实现。软件中断、系统输入等。

让我们以软件中断为例,使用printf()函数:
1-您的软件调用printf()。
2-printf()处理字符串和args,然后需要执行内核函数,因为在环3中无法写入文件。
3-printf()生成一个软件中断,将内核函数(在这种情况下是write()函数)的编号放入寄存器中。
4-软件执行中断,指令指针移动到内核代码。现在我们在环0中,在一个核函数中。
5-内核处理请求,写入文件(stdout是一个文件描述符)。
6-完成后,内核使用iret指令返回软件代码。
7-软件的代码继续


因此,C标准库的功能可以在C中实现。它所要做的就是知道如何在内核需要更多权限时调用它。

在Linux中,
strace
实用程序允许您查看程序进行的系统调用。所以,像这样的节目

int main(){ printf("x"); return 0; } 在跟踪的下一个最后一个调用中,橡胶与道路相交(排序,见下文):
write(1,“x”,1x)
。此时,控件从用户land
printx
传递到处理其余部分的Linux内核
write()
是在
unistd.h

extern ssize_t write (int __fd, __const void *__buf, size_t __n) __wur; 外部数据写入(int-fd,常量无效*,大小); 大多数系统调用都是以这种方式包装的。顾名思义,包装器函数只不过是一个薄代码层,它将参数放入正确的寄存器中,然后执行软件中断0x80。内核捕获中断,其余部分为历史记录。或者至少它以前是这样工作的。显然,中断捕获的开销相当高,正如前面的一篇文章所指出的,现代CPU架构引入了
sysenter
汇编指令,它以更快的速度实现了相同的结果。本页对系统调用的工作方式进行了很好的总结

我觉得你可能会对这个答案有点失望,就像我一样。显然,从某种意义上说,这是一个错误的底部,因为在调用
write()
和图形卡帧缓冲区实际修改为字母“x”之间仍有很多事情要发生出现在您的屏幕上。通过深入内核来放大接触点(保持“橡胶对道路”的类比),如果这是一项耗时的工作,那么肯定是有教育意义的。我猜您将不得不经历几个抽象层,如缓冲输出流、字符设备等 extern ssize_t write (int __fd, __const void *__buf, size_t __n) __wur;