本文已参与「新人创作礼」活动,一起开启掘金创作之路。
进程和线程
进程是程序的一次执行,进程是资源分配的最小单位,线程是cpu运行的最小单位,一个进程里可以包含多个线程
进程线程的切换
进程切换需要切换页目录和新的地址空间,切换内核栈和上下文 线程切换只切换栈和上下文
进程
一个进程能有几个堆,几个栈
堆有一个,栈各线程独占
进程的通信方式
父子进程 管道 无亲缘进程 有名管道 消息队列 消息队列中的消息有类型,读的时候可以按某种类型筛选 共享存储 效率高,少量数据拷贝,共享内存在内存里开辟一个空间,每个内存映射进来; mmap是在进程的内存里 信号量 进程同步互斥,用于有序的访问某个资源 信号 通知某个进程事件发生 socket
如何创建进程
fork()创建一个就绪态的进程,然后会进入系统调用sysfork,在内核态调度,之后sysclone,sysclone调用copyprocess复制一份父进程除了stack指针以外的全部信息,子进程记录父进程的标志,设置子进程的堆栈,之后调用schedfork把子进程状态设置为running然后调用copythread来复制线程,复制寄存器的值以及父进程的环境,然后子进程从父进程中分离出去,子进程有了pid号,之后就是wakeupnewtask子进程开始执行。 但实际上,由于copy on write原理,fork之后,内核会先把父进程中所有内存权限设置为readonly,子进程的地址空间指向父进程,父子进程如果都只读的话那就没事,如果某个进程要写内存,但这时候内存页式readonly,缺页中断,这时候内核会把页复制一份,父子各拿一份
fork{
do_fork(){
copy_process(){
dup_task_struct()复制过来要赋值,除了stack不同,其他都和父进程一样
sched_fork()初始化数据结构,状态设置为taskrunning
copy_thread()
}
wake_up_new_process()
}
}
进程调度策略
FCFS SJF 时间片轮转 优先级调度 高响应比优先 多级队列
为什么操作系统不是一个进程
进程内资源共享,多个线程共用一套资源,如果只有一个进程,线程之间就有互相修改数据的可能
线程
线程池
一个程序的运行就可以被看做一个进程,线程是程序运行中实际的任务执行者,线程池中有核心线程和非核心线程,如果有任务,先给核心线程,核心线程满了就放给任务队列,队列也满了,如果线程数没超,就给非核心线程,如果超了就拒绝,拒绝策略为,线程池满了/啥也不说/插队进队列头/execute
线程的同步
线程私有区和共享区
线程间栈共享,堆不共享
线程越多越好吗
当线程数较少时,cpu管理各线程不存在线程调度的上下文切换,当线程数增加时,线程在cpu间切换,线程切换需要切换栈和上下文,当程序运行的效率低于栈切换所消耗的资源时,效率就会降低,当线程数越来越大时,资源主要就浪费在了线程切换的消耗上
死锁
线程或进程使用某一资源时,需要对该资源独占,
死锁的四个条件
互斥:资源被独立占用 请求保持:阻塞但不释放资源 不剥夺:只能等自己释放 环路:必然存在一个环形请求资源链
预防死锁
一次性分配完资源 有一个资源没准备好,那别的资源我也不给你 我没得到所有的资源,就把已经得到的这部分资源释放了
如何排查并解决死锁
pstack & gdb
单核情况下考虑锁机制吗
考虑,单核心但进程数可以很多,只要有多进程就会死锁
多线程有哪些锁
读写锁的特点
乐观锁 & 悲观锁
自旋锁
页式 & 段式
用户态 & 内核态
逻辑地址怎么转换成物理地址
逻辑地址/页大小,得到页号,逻辑地址%页大小,得到页内偏移,根据页号找对应的块号