内核级线程Linux实现

————————————————————————————————————————————
并发处理,涉及资源的竞争和贡献,需要更好地理解多进程。

没有用户虚拟地址空间的进程的进程叫做内核线程。

每个内核线程需要创建一套栈:
在这里插入图片描述

1、创建 概览

1.1 INT中断进入内核

main代码里调用fork()
在这里插入图片描述
这里是多进程初始化函数,0x80指向的就是名字叫做system_call中断处理程序,即系统调用

fork()函数
1、把fork()对应的系统调用号赋值给eax寄存器
2、是一段包含INT 0x80的代码,引起中断进入内核:
3、返回值是eax寄存器里的内容
————————————————————————————————————————————
在这里插入图片描述
INT 0x80作用,1、把用户栈内容压到内核栈,2、执行中断处理程序
1、SS栈底,SP栈顶指针,压栈
2、EFLAGS表示一堆标志位,压栈
3、ret=??处,存储用户态的返回地址,即mov res,%eax,压栈
4、0x80对应system_call中断处理程序,即系统调用,接下来执行system_call

————————————————————————————————————————————

1.2 系统调用 调用_system_call代码:

在这里插入图片描述
1、ds,es,fs…寄存器入栈代码
2、call sys_fork,正式创建线程(第2节具体介绍)
3、调度? 判断当前线程状态state时间片counter,是否需要调度reschedule
4、执行ret_from_sys_call,返回用户态

————————————————————————————————————————————

1.3 返回用户态 ret_from_sys_call具体代码:

在这里插入图片描述
1、各寄存器ebx,ecx等出栈,作用是恢复现场,最终执行iret
2、iret作用是返回用户态,即返回到mov res,%eax执行,SS,SP重新指回用户栈,且eax值为0,代表子进程

INT指令和IRET指令是一对

————————————————————————————————————————————

若需要调度! 执行reschedule()函数:
在这里插入图片描述
1、先把ret_from_sys_call的地址入栈,作用是保存执行完调度函数之后,退出内核态代码的地址,再_schedule()因为如果不压栈的话,那么调度之后无法从内核态返回用户态了
2、_schedule()代码,先选下一个线程,再执行switch_to()

switch_to()代码,是平台相关的,第3节具体介绍
_schedule()代码,涉及进程调度,我会另外开一个博客介绍 to do

————————————————————————————————————————————

2、进程创建 fork

3个系统调用用来创建新进程:
(1)fork,fork创造的子进程是父进程的完整副本,复制了父亲进程的资源,包括内存的内容task_struct内容
(2)vfork,vfork创建的子进程与父进程共享数据段,而且由vfork()创建的子进程将先于父进程运行
(3)clone,这个系统调用的主要用处是可供pthread库来创建线程。

第1节以fork为例,宏展开后是sys_fork(void)

所有进程复制(创建)的fork机制最终都调用了kernel/fork.c中的_do_fork(一个体系结构无关的函数)

在这里插入图片描述

(1)复制进程描述符,copy_process()的返回值是一个 task_struct 指针
(2)将子进程加入到调度器中,为其分配 CPU,准备执行
(3)如果是 vfork,将父进程加入至等待队列,等待子进程完成

2.1 任务结构体复制

如下所示为copy_process()函数源码精简版,task_struct结构复杂也注定了复制过程的复杂性,因此此处省略了很多,仅保留了各个部分的主要调用函数

static __latent_entropy struct task_struct *copy_process(
          unsigned long clone_flags,
          unsigned long stack_start, unsigned long stack_size,
          int __user *child_tidptr, struct pid *pid, int trace,
          unsigned long tls, int node)
{
   
    int retval;
    struct task_struct *p;
......
    //分配task_struct结构
    p = dup_task_struct(current, node);  
......
    //权限处理
    retval 
  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Linux用户线程内核线程是两种不同的线程实现方式。 用户线程是由用户空间的线程库来实现的,线程的创建、调度、同步等操作都在用户空间中完成,内核并不知道线程的存在,因此线程的切换不需要切换到内核态,具有轻量、高效率等优点。但是由于用户空间线程库没有访问系统资源的权限,导致线程无法利用多核处理器等硬件资源。 内核线程是由内核来管理的,线程的创建、调度、同步等操作都在内核中完成,内核可以直接访问系统资源,因此线程可以充分利用硬件资源,但是由于线程的切换需要切换到内核态,具有较高的开销,同时线程数量过多也会导致内核资源消耗过大。 在Linux中,用户线程实现方式主要有两种:基于轻量进程(LWP)和基于协程。基于轻量进程的用户线程,是通过在用户空间中创建一组LWP来实现的,每个LWP对应一个内核线程,用户线程之间的切换通过LWP之间的切换来完成。基于协程的用户线程,是通过在用户空间中维护一组协程来实现的,每个协程对应一个用户线程,用户线程之间的切换通过协程之间的切换来完成。 而内核线程实现方式则是通过内核中的线程调度器来管理线程的运行,线程的创建、调度、同步等操作都在内核中完成,线程之间的切换也是在内核中完成。Linux中的内核线程实现方式主要有两种:基于进程的线程和基于线程线程。基于进程的线程是指每个线程都对应一个进程,由内核来管理进程和线程之间的关系;基于线程线程是指多个线程共享一个进程,由内核来管理线程之间的关系。在Linux中,基于线程线程实现方式更加常见,例如pthread库就是基于线程线程实现方式来实现线程编程的。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值