1.1 信息就是位+上下文
定义:
- 计算机系统由系统硬件与系统软件组成。
- 信息就是位+上下文。
- 只由ASCII字符组成的文件称为文本文件,其他所有的文件都称为二进制文件。
- C语言是一门系统级编程语言
#include <stdio.h>
int main (void)
{
printf("helllo world\n");
return 0;
}
扩展思考:
#include <stdio.h>
可以去掉吗?如果想去掉应该怎样写?- <>代表了什么意思?
1.<>与" "代表的是寻址方式的不同,前者是绝对路径,后者是相对路径,但是单纯的将<>替换为""不行哦
2.想要去掉#include<>stdio.h,首先要明白这里做了什么,向标准输出数据,这里其实会涉及到底层的io,系统调用,在第10章你们就会学到,所以可以直接调用系统调用来想标准输出输出hello world
1.2 程序被其他程序翻译成不同的格式
定义:
- 编译系统:预处理器,编译器,汇编器,链接器
- 预编译阶段:将以#开头的命令修改为原始的C程序,并生成以.为后缀的程序
gcc -E hello.c -o hello.i
841 # 943 "/usr/include/stdio.h" 3
842
843 # 8 "hello.c" 2
844
845 int main(void)
846 {
847 printf("hello world\n");
848 return 0;
849 }
- 编译阶段:编译器将文本文件hello.i翻译成hello.s,这是一个汇编语言程序
gcc -S hello.i -o hello.s
1 .file "hello.c"
2 .section .rodata
3 .LC0:
4 .string "hello world"
5 .text
6 .globl main
7 .type main, @function
8 main:
9 .LFB0:
10 .cfi_startproc
11 pushq %rbp
12 .cfi_def_cfa_offset 16
13 .cfi_offset 6, -16
14 movq %rsp, %rbp
15 .cfi_def_cfa_register 6
16 movl $.LC0, %edi
17 call puts
18 movl $0, %eax
19 popq %rbp
20 .cfi_def_cfa 7, 8
21 ret
22 .cfi_endproc
23 .LFE0:
24 .size main, .-main
25 .ident "GCC: (GNU) 4.8.5 20150623 (Red Hat 4.8.5-36)"
26 .section .note.GNU-stack,"",@progbits
- 汇编阶段:汇编器将hello.s翻译成机器语言指令,把这些指令打包成一种叫做可重定位目标程序的格式,保存在hello.o中,二进制文件
gcc -c hello.s -o hello.o
objdump -d hello.o
hello.o: 文件格式 elf64-x86-64
Disassembly of section .text:
0000000000000000 <main>:
0: 55 push %rbp
1: 48 89 e5 mov %rsp,%rbp
4: bf 00 00 00 00 mov $0x0,%edi
9: e8 00 00 00 00 callq e <main+0xe>
e: b8 00 00 00 00 mov $0x0,%eax
13: 5d pop %rbp
14: c3 retq
- 链接阶段:链接器负责合并各种库文件,例如汇编生成的目标文件吗,系统库文件,链接库文件,得到可执行文件hello,可以被加载到内存中执行
gcc hello.c -o hello //从源文件到目标文件
./hello
hello world
扩展阅读:
- 编译阶段需要检查代码的规范性,以及语法错误
1.3 了解编译系统如何工作是大有益处的
益处:
- 优化程序性能
#include<stdio.h>
#include <sys/time.h>
double now()
{
struct timeval tv = { 0, 0 };
gettimeofday(&tv, NULL);
return tv.tv_sec + tv.tv_usec / 1000000.0;
}
void addUseParam(size_t* num)
{
double start = now();
size_t i;
for(i = 0; i < 1000000000; i++)
{
*num += i;
}
printf("time = %lf for addUseParam\n", now() - start);
}
void addUseValue(size_t* num)
{
double start = now();
size_t i;
size_t value = 0;
for(i = 0; i < 10000000000; i++)
{
value += i;
}
*num = value;
printf("time = %lf for addUseValue\n", now() - start);
}
int main(void)
{
size_t num = 0;
addUseValue(&num);
addUseParam(&num);
return 0;
}
- 理解链接是出现的问题
- 避免安全漏洞
1.4 处理器读并解释储存在内存中的指令
- shell的工作原理
1.1.4 系统的硬件组成
- 总线:贯穿整个系统的是一组电子管道,称作总线。
- I/O设备:是系统与外界联系的通道,例如用于输入的键盘与鼠标,用于输出的显示器,以及保存数据的磁盘。
- CPU:中央处理单元,是解释存储在主存中的指令引擎
- ALU:算术/逻辑单元
- PC:程序计数器,处理器核心,任何时候都指向主存中的某条机器语言指令。
- USB:通用串行总线
- 主存:临时存储设备,用于在程序运行时存放程序与程序处理的数据。
- 物理构成:DRAM
- 逻辑构成:线性的字节数组,每个字节有唯一地址,从零开始。
- 指令集架构与微体系结构的区别:指令集架构描述每条机器指令执行的效果,微体系结构描述处理器是如何实现的。
1.5告诉缓存至关重要
- 高速缓存的实现:采用静态随机访问存储器实现(SRAM)
- 局部性原理:程序具有访问局部区域里的数据和代码的趋势
1.7 操作系统管理硬件
- 操作系统的功能:
- 防止应将被失控的应用程序滥用;
- 向应用程序提供简单一致的机制来控制复杂而又大不相同的低级硬件设备。
- 文件是对I/O设备的抽象
- 虚拟内存是对主存和磁盘I/O的抽象
- 进程是对处理器,主存和I/O设备的抽象
进程
- 进程是操作系统对一个正在进行的程序的一种抽象。
- 上下文切换:多个进程之间可以交错进行的这种机制
- 操作系统保存跟踪进程运行所需的所有状态信息,这种状态就是上下文。
- 从一个进程到另一个进程的转换是有操作系统内核来管理的。内核是操作系统常驻内存的部分。
线程
- 一个进程是有多个线程的执行单元组成,每个线程都运行在进程的上下文中,并共享同样的代码和全局数据。
虚拟内存
- 地址空间的最上面区域是保留给操作系统的代码和数据的,地址底层的区域存档的是用户进程定义的代码和数据,地址由下往上增大
- 栈:位于用户虚拟地址空间顶部的是用户栈,编译器用它来实现函数调用。
- 内核虚拟内存:地址空间顶部的区域是为内核保留的。
文件
文件就是字节序列。
1.9 重要主题
- 系统不仅仅是硬件,系统是系统硬件与系统软件的集合体,他们必须共同协作以达到运行应用程序的最终目的。
Amdahi定律
- 当我们对系统的某个部分进行加速时,其对系统整体性能的的影响取决于这部分系统的重要性和加速程度。
- 要想显著加速整个系统,必须提升全系统中相当大的部分的速度。
并发与并行
- 线程级并发:
- 多处理器的使用可以从两方面提高系统性能:
- 减少了在执行多个任务时模拟并发的需要。
- 使程序运行的更快。
- 指令级并行:
- 流水线
- 超标量
- 单指令、多数据并行
- 硬件允许一条指令产生多个可以并行执行的操作。
- 抽象的使用是计算机科学中最重要的概念,例如为一组函数规定一个简单的API就是一个很好的编程习惯。