scanf()在操作系统内部是如何工作的?

10

我一直在想scanf()/printf()在硬件和操作系统级别上是如何工作的。数据流向何处,操作系统在这些时候到底在做什么?操作系统做出了哪些调用?等等...


2
你在谈论哪个操作系统? - Billy ONeal
我并没有特定的操作系统要求。Linux/Unix类型的系统都可以。我只是想对情况有一个基本的了解。 - jetru
1
它们通常不是操作系统的一部分,而是C库(libc、glibc等)的一部分。scanf()使用POSIX read(),printf使用POSIX write()。一些标准的Unix编程书籍应该能够在这个层次上提供帮助,比如《UNIX环境高级编程》(APUE)。 - dajobe
4个回答

27

scanf() 和 printf() 是 C 标准库 libc 中的函数,分别调用 read() 和 write() 操作系统系统调用,并分别与文件描述符 stdin 和 stdout 通信 (fscanf 和 fprintf 允许您指定要从中读取/写入的文件流)。

对 read() 和 write() (以及所有系统调用) 的调用会导致 '上下文切换',从用户级应用程序切换到内核模式,这意味着它可以执行特权操作,如直接与硬件通信。根据应用程序的启动方式,'stdin' 和 'stdout' 文件描述符可能绑定到控制台设备 (如 tty0),或某种虚拟控制台设备 (如 xterm 提供的)。read() 和 write() 安全地复制数据到/从称为 'uio' 的内核缓冲区。

scanf 和 printf 的格式字符串转换部分不是在内核模式下发生,而只是在普通用户模式 (libc 内部) 中进行。关于系统调用的一般经验法则是尽可能少地切换到内核模式,既可以避免上下文切换的性能开销,还可以保证安全性(在内核模式下发生的任何事情都需要非常小心!内核模式中的代码越少,操作系统中的错误和安全漏洞就越少)。

顺便说一句..所有这些都是从 Unix 的角度写的,我不知道 MS Windows 如何工作。


1
读取和写入系统调用需要通过UIO作为参数传输的字节数,因此它不必为每个单独的字节进行单独的系统调用。你可能认为对于像getchar()这样的简单输入函数,每个字符都需要单独调用,但实际上,现在的libc比那聪明得多,并且它保留了一个缓冲区(在libc内部)。因此,它可以通过填充缓冲区来避免上下文切换的性能开销,然后每次你使用getchar()或scanf()处理一点缓冲区内容,直到缓冲区为空,然后才进行另一个系统调用。 - David Claridge
哇,这听起来不错。操作系统如何将字节从键盘传输到其UIO缓冲区?read()和write()调用可以实现,但是从哪里开始?字节来自键盘吗?键盘驱动程序? - jetru
read() 不知道键盘,它处于稍高的抽象层面,它只知道它所连接的设备节点,例如控制台设备。该设备的驱动程序将在文件系统中提供一个节点,以便 read() 可以与之通信,而实际上从硬件中获取字符的能力则需要由驱动程序来提供。 - David Claridge
1
对于许多简单的 C 代码而言,stdio 函数如 scanfprintf 幸运地不会每次都调用 readwrite。它们(通常来说,我在简化)实际上维护一个缓冲区,并尽可能减少系统调用的次数以减少内核进出的切换。典型的 Windows libc 实现方式也非常类似,但使用了 ReadFileWriteFile 系统调用。内核内部的详细细节不同,但基本的抽象和整体数据流非常相似。 - RBerteig
@DavidClaridge:非常好的答案。我想了解一下它们如何在MS Windows操作系统中工作。 - Destructor
显示剩余8条评论

1

在我的操作系统上,我正在使用基于函数getch()和putch()的scanf和printf。


哇,那真是太棒了。我成功地将输出从UART转换为TCP/IP,当只有单个客户端连接时。这是一个简单的非抢占式合作式嵌入式系统微内核。 - Luka Rahne

0
我认为操作系统只提供了两个流,一个用于输入,另一个用于输出。这些流将输出数据的呈现方式或输入数据的来源抽象出来。
所以scanf和printf实际上只是从这些流中添加字节(或消耗字节)。

这是高层抽象。我希望了解这些流如何与硬件配合工作以及操作系统如何管理所有数据的详细信息。 - jetru

0

在编程中,scanf、printf等函数不能直接用C/C++语言编写。实际上,它们都是通过使用关键字“asm”在汇编语言中编写的。任何使用关键字“asm”编写的内容都会直接引入目标文件,无论是否编译(即使编译后也不会改变)。在汇编语言中,我们有预定义的代码可以实现所有这些函数......因此,简而言之,SCANF PRINTF等所有函数都是在内部以汇编语言编写的。您可以使用关键字“ASM”设计自己的输入函数。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接