CS:APP 第一章:计算机系统漫游(1)

前言

大一的时候买了深入理解计算机系统(CS:APP),当时有些地方看得不是很理解,而且由于时间长了也有点忘了。这次回家正好带了这本书,于是准备重新读一遍并记录一下自己学习的过程(其实只是抄抄书,浓缩一下书上的内容,再加一点自己想的还有一些乱七八糟的东西罢了hhhh)。

在CS:APP第一章中,hello程序作为讲解的示例,即我们最为熟知的hello world。

#include <stdio.h>

int main()
{
    printf("hello, world\n");
}

1.1 信息就是位+上下文

程序的生命周期从源文件开始,即使用文本编辑器输入上述hello程序保存的文本文件。源文件实际上是由0和1组成的位序列,8个位组成一组成为字节。
在英文系统中,使用ASCII码即可表示大部分需要的字符,ASCII码中每一个字节代表一个字符。而在中文系统中,由于汉字的个数远大于英文字母的个数,单个字节代表一个字节是不足以容纳所有的汉字的(1个字节最多仅能2^8个字符,即256个)。所以在ASCII码的基础上,出现了许多用于其它语言的字符集,如我们常用的UTF-8等。下图是本文其中的一段文字,通过emacs的hexl-mode模式查看字符是如何以十六进制表示的。
十六进制模式下的字符
在图中我们可以发现,在UTF-8中,汉字以及全角状态下的符号是以三个字节表示的,而UTF-8中英文字符仍然以一个字节表示。由于字符集并不是这里的重点,就不再在此深入讨论。


1.2 程序被其他程序翻译成不同的格式

在Unix/Linux中,我们通常使用gcc来编译程序,以生成能够执行的二进制文件。

$gcc -o hello hello.c

在编译的过程中,需要通过预处理、编译、汇编、链接四个阶段处理,执行这四个阶段的程序一起构成了编译系统。
编译系统
1.预处理(Preprocessing)
预处理器主要处理以#开头的命令,如#include#define#if等等。如在hello程序中,在预处理阶段根据程序第一行的#include <stdio.h>读取stdio.h中的内容并将其插入到程序文本中,生成通常以.i作为后缀的C程序文件。可以使用gcc的-E命令输出预编译的结果。

$gcc -E hello.c -o hello.i

2.编译(Compilation)
编译器的作用主要是将预处理后的.i文件中的每条语句编译得到汇编语言指令,并保存在通常以.s作为后缀的汇编语言文件中,该文件也是文本文件。我们可以用gcc的-S命令输出编译结果。

$gcc -S hello.i -o hello.s

除了使用hello.i进行编译以为,也可以只是使用我们最原始的hello.c文件,只需要将命令中的hello.i替换成hello.c即可。以下即hello.c文件编译得到的汇编文件。

    .file   "hello.c"
    .def    ___main;    .scl    2;  .type   32; .endef
    .section .rdata,"dr"
LC0:
    .ascii "hello, world\0"
    .text
    .globl  _main
    .def    _main;  .scl    2;  .type   32; .endef
_main:
LFB6:
    .cfi_startproc
    pushl   %ebp
    .cfi_def_cfa_offset 8
    .cfi_offset 5, -8
    movl    %esp, %ebp
    .cfi_def_cfa_register 5
    andl    $-16, %esp
    subl    $16, %esp
    call    ___main
    movl    $LC0, (%esp)
    call    _puts
    leave
    .cfi_restore 5
    .cfi_def_cfa 4, 4
    ret
    .cfi_endproc
LFE6:
    .ident  "GCC: (GNU) 4.8.1"
    .def    _puts;  .scl    2;  .type   32; .endef

3.汇编(Assembly)
在汇编阶段中,汇编器将编译阶段得到的汇编文件翻译成机器语言指令,打包成可重定位目标程序的格式,并通常将结果保存在.o作为后缀的文件中。在此阶段后得到的.o文件就已经是一个二进制文件,若使用文本编辑器打开将会看到乱码。在gcc中可以使用-c选项输出汇编结果。

$gcc -c hello.s -o hello.o

下图是通过emacs的hexl-mode模式查看汇编得到的目标文件。
十六进制表示的目标文件
4.链接(Linking)
在hello程序中,调用了一个名为printf的函数,这是C编译器都会提供的标准库中的一个函数,存在与名为printf.o的目标文件中。在操作系统中通常不会直接存在printf.o文件,而是一般存在于后缀为.so(Linux)或.dll(Windows)的动态链接库文件中。链接器的作用即将hello.o的内容与printf.o的内容合并生成一个可执行文件hello(在Windows中为hello.exe)。


1.3 了解编译系统如何工作是大有益处的

这一小节本来要放到下一部分的,不过看了一下没什么重要的内容就把它加到了这一部分里。在这一小节中介绍了了解编译系统能更好的优化程序性能、理解链接时出现的错误、避免安全漏洞。
(这一节只是来充字数的hhhh)


其它

文中内容主要源自深入理解计算机系统第二版,部分图片来自该书中。

原创文章 29 获赞 31 访问量 7万+

猜你喜欢

转载自blog.csdn.net/ghosind/article/details/48002923