open-courses
  • 公开课笔记
  • CMU 15-445/645 Database Systems
    • Relational Data Model
    • Advanced SQL
    • Database Storage
    • Buffer Pools
    • Hash Tables
    • Tree Indexes
    • Index Concurrency Control
    • Query Processing
    • Sorting&Aggregations
    • Join Algorithms
    • Query Optimization
    • Parallel Execution
    • Embedded Database Logic
    • Concurrency Control Theory
    • Two Phase Locking
    • Timestamp Ordering Concurrency Control
    • Multi-Version Concurrency Control
    • Logging Schemes
    • Database Recovery
    • Introduction to Distributed Databases
    • Distributed OLTP Databases
    • Distributed OLAP Databases
  • UCB - CS162
    • OS intro
    • Introduction to the Process
    • Processes, Fork, I/O, Files
    • I/O Continued, Sockets, Networking
    • Concurrency: Processes & Threads
    • Cooperating Threads, Synchronization
    • Semaphores, Condition Variables, Readers/Writers
    • Scheduling
    • Resource Contention & Deadlock
    • Address Translation, Caching
    • File System (18,19,20)
    • Distributed Systems, Networking, TCP/IP, RPC (21,22)
    • Distributed Storage, Key-Value Stores, Security (23)
    • Security & Cloud Computing (24)
    • Topic: Ensuring Data Reaches Disk
  • MIT - 6.006
    • Sequence and Set Interface
    • Data Structure for Dynamic Sequence Interface
    • Computation Complexity
    • Algorithms and Computation
    • Structure Of Computation
    • Graph & Search
    • Tree & Search
    • Weighted Shortest Paths
    • String Matching, Karp-Rabin
    • Priority Queue Interface & Implementation
    • Dictionary Problem & Implementation
    • Sorting
    • Dynamic Programming
    • Backtracking
    • Self-Balancing Tree
  • MIT - 6.824
    • 2PC & 3PC
    • Introduction and MapReduce
    • RPC and Threads
    • Primary/Backup Replication
    • Lab: Primary/Backup Key/Value Service
    • Google File System (GFS)
    • Raft
    • Lab: Raft - Leader Election
    • Lab: Raft - Log Replication
  • Stanford-CS107
    • 原始数据类型及相互转化
    • 指鹿为马
    • 泛型函数
    • 泛型栈
    • 运行时内存结构
    • 从 C 到汇编
    • 函数的活动记录
    • C 与 C++ 代码生成
    • 编译的预处理过程
    • 编译的链接过程
    • 函数的活动记录续、并发
    • 从顺序到并发和并行
    • 信号量与多线程 1
    • 信号量与多线程 2
    • 复杂多线程问题
    • 函数式编程 - Scheme 1
    • 函数式编程 - Scheme 2
    • 函数式编程 - Scheme 3
    • 函数式编程 - Scheme 4
    • 函数式编程 - Scheme 5
    • Python 基础
  • MIT - 6.001 - SICP
    • 什么是程序
    • 程序抽象
    • 替代模型
    • 时间/空间复杂度
    • 数据抽象
    • 高阶函数
    • Symbol
    • 数据驱动编程与防御式编程
    • 数据抽象中的效率与可读性
    • 数据修改
    • 环境模型
    • 面向对象-消息传递
    • 面向对象 - Scheme 实现
    • 构建 Scheme 解释器
    • Eval-Apply Loop
    • Normal Order (Lazy) Evaluation
    • 通用机
    • 寄存器机器
    • 子程序、栈与递归
    • 在寄存器机器中执行
    • 内存管理
  • MIT - 6.046
    • Randomized Algorithms
    • Skip Lists
  • System Design
    • Twitter
    • Cache Consistency & Coherence
  • DDIA 笔记
    • Replication
    • Transactions
    • The Trouble with Distributed Systems
    • Consistency & Consensus
  • Papers We Love
    • Consistent Hashing and Random Trees (1997)
    • Dynamic Hash Tables (1988)
    • LFU Implementation With O(1) Complexity (2010)
    • Time, Clocks, and the Ordering of Events in a Distributed System (1978)
    • Dapper, a Large-Scale Distributed Systems Tracing Infrastructure (2010)
    • Gorilla: A Fast, Scalable, In-Memory Time Series Database (2015)
  • Release It 笔记
    • Anti-patterns & Patterns in Microservice Architecture
  • Database Design
    • Log Structured Merge (LSM) Tree & Usages in KV Stores
    • Prometheus
Powered by GitBook
On this page
  1. Stanford-CS107

函数的活动记录续、并发

第十四课

Previous编译的链接过程Next从顺序到并发和并行

Last updated 6 years ago

函数的活动记录续

例1:上一节课最后一例

int fn() {
    int array[4];
    int i;
    for (i=0; i<=4; i++) {
        array[i] -= 4;
    }
    return 0;
}

函数的活动记录如下图所示:

由于 c 语言编译过程没有越界检查,且 array[3] + 4 地址处存着执行完 fn 函数之后应该执行的命令地址,即图中虚线箭头指向的地址,但由于 array[4] -= 4 的操作使得 saved pc 自减 4,再次指向 fn 函数,因此 fn 将被重复执行,陷入死循环。需要注意的是,本例中谈论的编译过程并不使用于所有情况,不同的编译器会采取不同内存模型,不同的内部参数、返回地址存放方式,本例只是展示可能的一种情况。

例2:

int main() {
    DeclareAndInitArray();
    PrintArray();
}

void DeclareAndInitArray() {
    int array[100];
    int i;
    for (i=0; i<100; i++) {
        array[i] = i;
    }
}

void PrintArray() {
    int array[100];
    for (i=0; i<100; i++) {
        printf("%d\n", array[i]);
    }
}

DeclareAndInitArray() 初始化长度为 100 的 array 后,返回 main 函数,但在离开之前它并不会打乱这个 array 里的信息,当 PrintArray 执行的时候,新声明的 array 恰好占据了 DeclareAndInitArray 初始化好的 array 的空间,因此能够将对应的数值打印出来。

例3:printf 如何接受任意数量参数

int printf(const char *control, ...);

printf("%d + %d = %d\n", 4, 4, 8);

printf 可以接受任意数量的参数,它的函数原型如第一行代码所示。执行第二行代码时,函数的活动记录如下图所示:

在调用 printf 前,调用者会将参数从右到左压入栈中,printf 开始执行时,它并不知道 saved pc 上面有多少个参数,但它知道它能从第一个 char * 类型的参数中知道上面还有多少个参数。因此 printf 会根据字符串 control 的内容来按需取用 control 上方的内存信息,这就是接受任意数量参数的函数的工作原理。

调用者将参数从右向左压入函数栈中是一件比较反直觉的做法,如果调用者将参数从左到右压入栈中,函数的活动记录就会如下图所示:

这时候 printf 就无从得知 control 的位置,也就无法从中读出实际参数数量,因此本例也解释了参数入栈顺序的原理。

例4:struct 的多态

struct type {
    int code;
}

struct type-one {
    int code;
    //...
}

struct type-two {
    int code;
    //...
}

如果我们的函数需要接受一个结构体,这个结构体可能是两种类似但有所不同的结构体中的一种,比如 ipv4 的 header 与 ipv6 的 header,这时我们就需要在函数中能有某种方式来判断接收到的结构体具体是哪一种。与例3中的原理类似,利用结构体中的第一个成员变量来判断结构体中剩下的信息的种类就可以实现。

并发

单核 CPU 能够通过时间片轮转的方式做到多个程序并发运行,从而让计算机的使用者感觉到多个程序正 “同时” 运行。这时候,计算机的内存模型可以概括为下图:

图中有 gcc, make, firefox, lock 四个程序,逻辑上它们分别有自己独立的 stack segment、heap segment 以及 code segment,这些 segments 最终会被内存管理系统映射到内存中的对应地址上,从而实现多个程序在内存中并发运行。

而对于同时运行多个相同的任务的情况,如同时下载两首歌,其下载逻辑,即 code segment 在内存中实际上是共用的,只是 stack segment 和 heap segment 被映射到不同的位置。

参考

Stanford CS107: lecture 14