复习
本次课回答的问题
本次课主要内容
如果假设 sum.c 中的 sum++
是如下构成:
t = atomic_fetch(sum)
t++
atomic_store(sum, t)
那么 $k$ 个线程,输出的最小 sum
是多少?
结论有些反直觉:
sum
的最小值都是 2Verifying Sequential Consistency (VSC) is NP-Complete
课后习题难度 (3-SAT)
“加强豪华版课后习题” 😂
VSC 的变种 | 复杂度 |
---|---|
刚才的证明 (一般情况) | NP-Complete |
每个线程只执行 2 个操作 | NP-Complete |
只有 2 个变量 | NP-Complete |
只有 3 个线程 | NP-Complete |
每个读知道写者 | NP-Complete |
每个变量只被一个线程写入 | NP-Complete |
投机取巧的方法:
void Tworker() {
while (!feof(stdin) && scanf("%d", &x) == 1) {
long res = f(x);
lock(&lk);
sum += res;
unlock(&lk);
}
}
如果限制只有一个线程可以读,那就需要生产者-消费者了
正确率 (OJ 实时统计通过似乎只统计了编程题……)
87.5% (56/64) 的问卷表示 “没有出卖灵魂”
状态机的复制
熟悉又陌生
int open(const char *pathname, int flags);
O_CLOEXEC
, O_APPEND
文件描述符:一个指向操作系统内对象的 “指针”
write(3, "a", 1); write(3, "b", 1);
fd = open("a.txt", O_WRONLY | O_CREAT); assert(fd > 0);
pid_t pid = fork(); assert(pid >= 0);
if (pid == 0) {
write(fd, "Hello");
} else {
write(fd, "World");
}
文件抽象的代价
概念上状态机被复制,但实际上复制后
想证明这一点?
帮助我们
fork() 可以复制状态机?
假设你实现的 NEMU 需要启动很多份
./nemu dummy.elf
./nemu add.elf
./nemu add-longlong.elf
...int main() {
nemu_init(); // only once
while (1) {
file = get_start_request();
if ((pid = fork()) == 0) {
// bad practice: no error checking
load_file();
}
...
在实际中的应用
要是我们总是能 “试一试”,试错了还能回到过去就好了
那就用 fork() 做个快照吧
fork()
: UNIX 时代的遗产fork + execve
在操作系统的演化过程中,为进程增加了更多的东西
int posix_spawn(pid_t *pid, char *path,
posix_spawn_file_actions_t *file_actions,
posix_spawnattr_t *attrp,
char * argv[], char * envp[]);
参数
pid
: 返回的进程号path
: 程序 (重置的状态机)file_actions
: open, close, dupattrp
: 信号、进程组等信息argv
, envp
: 同 execve
fork()
in the Roadfork() 的七宗罪
但 fork() 是魔法啊:这引起了更多的思考
本次课回答的问题
Take-away messages