Skip to main content

多线程编程(入门版)

这么早开jyy的os课主要还是CTF的逼迫,pwn对于选手的知识面要求还是很广的,其次就是之后的PA3,虽然暂时离我比较遥远,所以这只是其次原因,总之,相较于10月份的那个懵懵懂懂的我来说,现在看jyy的os课也不会觉得太困难,不过这个时候我也只需要浅浅了解一下os即可,了解到能做pwn题的程度吧,至于真正的os编程或者在硬件上和os交互还得要更深入的看视频以及做jyy提供的课程习题,废话不多说,这篇文章只是这个视频的一些笔记多处理器编程:从入门到放弃 (线程库;现代处理器和宽松内存模型) [南京大学2022操作系统-P3]_哔哩哔哩_bilibili,为啥选择2022呢,一是疫情上网课画质清晰,二是我打算把2023或2024留给后面的我,三是我看2023的将这个放在了p5害怕有前置

Hello, Multi-threaded World

理解并发

形象理解并发程序,可以将其想象成一个教室,教室中每一个人就是一个线程,每一个有独立的行为思考,并且教室中还有一些共享的东西,比如黑板,粉笔,拖把….,这些就类似于全局变量。还可以用足球比赛来理解,每一个球员在运球(运行)时都需要考虑别的球员(线程)的行为,导致并发程序十分复杂

状态机视角理解并发程序,将程序看作状态机,单线程的程序的状态就是全局bss变量,heap,stack….,状态的切换就依靠pc以及指令,所以单线程的程序运行是确定的,多线程程序即并发程序,多个线程有着自己的stack,自己的pc,自己的指令,但是还有着共享的heap,全局变量,导致这个程序的下一个状态不确定,因为程序自己也无法保证下一条执行的指令属于哪个线程,并且这时候每一个线程都有着自己的状态,(即自己的stack+全局状态部分)

相关库函数

pthread_create()

即创造一个新的thread,从状态机的视角来看就是创建一个新的stack,创建一个新的pc指向某个函数(即某个指令)

pthread_join()

即线程的等待,从状态机视角来看,若是一个thread调用了此函数,那么若是下一条指令发生在改thread,该指令不会改变该thread的状态,知道其他thread全部结束,则该thread结束

__thread变量声明

即声明一个线程局部变量,即thread local store

原子性的丧失

emmm,并发程序由于都有对全局变量有访问的权力,导致了一些在单线程情况下不会发生的问题,比如若是两个线程由于一个全局变量的条件(比如说钱的余额)同时进入一个判断成功的分支中(即余额足够,但其实第一个扣了钱之后第二个是不够的),于是他就会执行两次扣钱操作,造成意想不到的bug,甚至wrap around。这就是一些难以处理的并发问题

根本原因在于程序独占处理器cpu的假设不在成立,看这个程序

每次都有不一样的结果,原因是假如sum在比方说一个初始值100时被两个线程同时读取,两个线程同时对其值进行计算,最后可能写入的时间不一样,但都是加一,就会导致前一个的结果被后一个的结果覆盖,导致结果为101而不是102,这就是原子性的丧失

顺序的丧失

编译器对于程序大多是单线程优化的,比如他会不根据c语言的语义来安排顺序,比如我说循环100此,每次让一个全局变量加一,编译器优化成现在寄存器中加一,最后再访问内存,这样可以减少io的损耗,这在单线程程序是没有问题的,可是在多线程中,可能会因为不同编译器的优化方式不同,导致同一份程序在不同编译器优化下产生不一样的结果

可见性的丧失