P1-4南京大学操作系统学习笔记

南京大学操作系统学习笔记

B站视频

1、绪论

只需要记住操作系统是为了实现多个程序看起来独占运行

2、应用眼中的操作系统

1:46 什么是应用程序
以elf格式存储 头部+其他节
23:33 应用程序怎么调用操作系统
31:29 工具观察程序的执行 --调试器 gdb命令 layout asm指令调出

gdb a.out
layout asm
si  //单步执行

44:33 objdump命令
52:20 main()之前发生了那些操作系统API 调用
工具strace
59:58 demo1:gcc调用的操作系统的API(生成其他进程)
strace -f gcc a.c 2>&1 | grep execve
65:50 demo2:图形界面xedit调用的系统API strace xedit(read 和write数据)

3、多处理器编程

1:55 并发与并行的概念
操作系统可以同时加载多个程序:每个程序都有独立的内存空间,互不干扰;每隔一段时间,切换到另一进程执行(看起来同时运行)

6:08 并发性的来源
进程调用操作系统的API
举例write说明
10:19 典型的并发系统
主要关注***多处理器、共享内存***情况
在这里插入图片描述
11:50 线程定义
并行执行、共享内存、执行顺序不确定在这里插入图片描述
15:00 线程间什么共享、什么不共享
资源包括:

  1. 代码资源(指令序列)
  2. 寄存器资源(rip,rsp,rax等等)
    各个寄存的功能介绍x86寄存器问题
  3. 局部变量所在的堆栈、全局变量
    其中,代码资源、全局变量数据共享,每个线程的堆栈和寄存器都是独享

20:16 POSIX的线程库

查看线程库手册

man 7 pthreads

28:44 线程创建 threads.h的实现

#include <stdlib.h>
#include <stdio.h>
#include <assert.h>
#include <pthread.h>

struct thread{   //链表
  int id;//线程id 线程号 从1开始
  pthread_t thread;//POSIX线程库规定的线程号
  void (*entry)(int); //线程的入口地址
  struct thread *next;//指向下一个线程
};

struct thread *threads;  //单恋表头
void (*join_fn)();

__attribute__((destructor)) static void join_all(){ //main函数结束之后调用 
  for(struct thread *next;threads;threads=next){   //遍历整个进程链表
    pthread_join(threads->thread,NULL);
    next=threads->next;
    free(threads);
  }
  join_fn?join_fn():(void)0;
}

static inline void *entry_all(void *arg){//符合pthreads_create要求
  struct thread *thread =(struct thread *)arg;//取出线程对象指针cur
  thread->entry(thread->id);//调用该线程的entry,对线程入口赋值
  return NULL;//
}

//线程创建
static inline void create(void *fn){
  struct thread *cur=(struct thread *)malloc(sizeof(struct thread));//分配内存
  assert(cur);//假设内存分配成功
  cur->id=threads?threads->id+1:1;//当钱线程的线程号
  cur->next=threads;
  cur->entry=(void (*)(int))fn;
  threads   =cur;
  pthread_create(&cur->thread,NULL,entry_all,cur);
}

static inline void join(void (*fn)()){  
  join_fn=fn;  //将函数指针赋值给全局变量
}

28:57两个多线程程序测试:
1、创建两个线程,分别打印a和b a.c
编译命令 gcc b.c -I. -lpthread^C

#include "threads.h"

void a() {while(1)  printf("a");}
void b() {while(1)  printf("b");}

int main(){
  setbuf(stdout,NULL);  
  create(a);
  create(b);
}

2、创建1000个线程,证明全局变量共享

#include "threads.h"

void f(){
  static int x=0;  //共享x
  printf("hello from thread #%d\n",x);
  while(1);
}

int main(){
 for (int i=0;i<1000;i++)
    create(f);
  join(NULL);
}

35:00 用代码证明了pthread给每个线程分配了8M的堆栈。没有导致内存不够是因为内存虽然分配,但是只是在线程的地址空间标记了一下,没有真正分配。

4、多处理器编程的困难

41:30 共享资源
并发/并行执行的线程可能对资源内存进行争抢

示例1:输出的并不是2n,这就是因为sum++可以分解为三条指令,交替执行
①->②->...->⑥执行顺序

#include "threads.h"

long sum =0 ;

void do_sum(){
  for(int i=0;i<100000000;i++)
    sum++;
  //printf("sum=%ld\n",sum);
}

void print(){
  printf("2sum=%ld\n",sum);
}

int main(){
  create(do_sum);
  create(do_sum);
  join(print);
}

示例2:不同的编译优化等级输出不同
使用不同的编译优化对sum.c进行编译,输出的结果不同

71:00 保证原子性(本课程重点)

理解并发程序执行的工具

在这里插入图片描述

1、串行程序的状态机模型
A、状态机的概念
B、程序=有限状态机

寄存器看成状态机的节点;程序指令的执行看成状态机的边
11:34 不确定的指令可能有多个后续状态
不确定性的来源比如:在这里插入图片描述
19:33 X86-64的寄存器
X86-64的寄存器
这些寄存器的状态可以被gdb观察到,gdb中运行下面的命令:

info registers

查看进程的进程号:info inferiors

22:32 状态机模型的应用
A、超标量处理器,允许在状态机跳跃
B、程序分析技术
gdb step过头了,但是记录了状态机,就可以跳转到之前的状态
C、记录和重放
对于确定性的指令,只需要记录初始状态和执行步骤,就可以实现回放;
对于不确定的指令(syscall) 还需要记录这条指令的结果,重放时将记录的结果当做syscall的返回值

37:36 并发程序的状态机模型
在每一个时刻,都可以选择任何一个线程程序向前执行一步,导致并发程序的状态机很大
在这里插入图片描述
45:00 理解并发程序的执行
peterSon算法,直接理解代码:
在这里插入图片描述
构造状态机:(PC表示指令指针,PC1=1表示第一个线程执行到指令1)
在这里插入图片描述
那么判断一个并发程序正确,有两个条件:

  • 不存在错误状态(这里是PC13^PC22)
  • 不存在无穷路径(状态图有环)
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值