第二篇. 操作系统之 进程与线程

五.多进程图像的引出L8: CPU管理的直观想法不仅要切换PC指针,还需要对应的寄存器环境L9: 多进程图像六、 线程引出与实现L10:用户级线程10.1 需要两个栈用于线程切换几个线程就需要几个栈 10.2 用户主动切换只在调用Yield处进行切换 yield, thread_create都是自己写的,linux 0.11源码中没有10.3 进程和线程概念区分进程 和 线程 都是动态概念 进
摘要由CSDN通过智能技术生成

五.多进程图像的引出

L8: CPU管理的直观想法

不仅要切换PC指针,还需要对应的寄存器环境

L9: 多进程图像

title

六、 线程引出与实现

L10:用户级线程

10.1 需要两个栈用于线程切换

几个线程就需要几个栈
title

10.2 用户主动切换

只在调用Yield处进行切换
yield, thread_create都是自己写的,linux 0.11源码中没有

10.3 进程和线程概念区分

进程 和 线程 都是动态概念
进程 = 资源 (包括寄存器值,PCB,内存映射表)+ 指令序列
线程 = 指令序列

线程 的资源是共享的,
进程 间的资源是分隔独立的,内存映射表不同,占用物理内存地址是分隔的

线程 的切换只是切换PC,切换了指令序列
进程 的切换不仅要切换PC,还包括切换资源,即切换内存映射表

用户级线程:调用Yield函数,自己主动让出cpu,内核看不见,内核只能看见所属进程而看不见用户级线程,所以一个用户级线程需要等待,内核会切到别的进程上,不会切到该进程下的其他用户级线程!!!
内核级线程: 内核能看见内核级线程,一个线程需要等待,内核会切到所属进程下的其他内核级线程。

L11:内核级线程

11.1 基本概念

title
只有内核级线程才能发挥多核性能,多个CPU共用一套MMU设备情况下,可以一个CPU执行一个内核级线程,在内核级线程切换的时候,不需要切换内存映射关系,代价小很多,因为本来一个进程中MMU映射关系就是一样的
进程 无法发挥多核性能,因为只有一套MMU,进程切换时MMU映射关系也得跟着切换,即切换内存映射表,切换内存映射表代价比较大

一个内核级线程的切换需要两套栈: 用户栈 + 内核栈
title
title

注意
linux 0.11中本身不支持内核级线程切换,只有进程切换,但实际上只是多了切换内存映射表那部分。

11.2 完整的系统调用中断过程:

1) INT中断自动压栈的有下一条指令,以及用户级线程SS:SP,就是下图五个参数

2) _system_call 把寄存器保护压栈是压到内核栈中,需要手动压栈
3) 系统调用,(有可能是_sys_fork,其实就是根据标号找到的系统调用),结束之后继续执行,要执行reschedule,先push $ret_from_sys_call,让其在_schedule之后返回到ret_from_sys_call, _schedule为c函数,结束右括号会把ret_from_sys_call pop出来,返回到这里执行,即执行ret_from_sys_call;
这里注意call 和 jmp的区别!!!
4)在ret_from_sys_call中pop出_system_call时保护的寄存器内容,然后中断返回!!!
5)中断返回是在最后,中断返回会把SS:SP 以及用户态的下一条指令 POP出来,即把5个寄存器pop出来!!!这样就会返回到用户栈,运行用户态的下一条指令!!!

代码:

reschedule:
    pushl $ret_from_sys_call
    jmp _schedule                              //如果用call就是先将下一条指令压栈,然后jmp,这样只能顺序往下走,但是用push+jmp则可以改变跳转地址!!!
11.3 switch_to五段论


这里要注意,中断出口这里已经经过了前面的switch_to,中断的iret已经不是原先的中断返回了,是切换后的新中断的执行返回!!!这样返回以后就来到了引发该新中断的用户态代码来执行

L12 核心级线程实现实例

12.1 fork函数经典调用
if (!fork())          //关键在于 INT 80 后面的指令 mov res, %eax;父进程和子进程都会执行这个代码,但是%eax的值不一样
{
     子进程
}
else
{
     父进程
}

fork新建了内核栈,但没有新建用户栈,即子进程和父进程共用了用户栈,这样不会出现问题么???
title

//Main.c
static inline _syscall0(int,fork) 

//宏定义
#define _syscall0(type,name) \
type name(void) \
{ \
long __res; \
__asm__ volatile ("int $0x80" \
    : "=a" (__res) \
    : "0" (__NR_##name)); \
if (__res >= 0) \
    return (type) __res; \
errno = -__res; \
return -1; \
}

//宏展开
int fork()
{
long __res; \
__asm__ volatile ("int $0x80" \
    : "=a" (__res) \
    : "0" (__NR_fork)); \
if (__res >= 0) \
    return (type) __res; \
errno = -__res; \
return -1; \
}
12.2 sys_fork解析
sys_fork:
    call find_empty_process
    testl %eax,%eax
    js 1f
    push %gs
    pushl %esi
    pushl %edi
    pushl %ebp
    pushl %eax
    call copy_process
    addl $20,%esp
1:  ret
12.3 find_empty_process

find_empty_process返回非0值,要么为正整数,要么为负数,并且将返回值保存到寄存器eax中,即父进程的eax值为非0<

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值