Linux内核设计与实现

一. linux内核简介

1. linux简介

1.1 unix的特点

  • unix很简洁,仅提供几百个系统调用,并有非常明确的设计目的
  • unix所有东西都当作文件对待,这种抽象使对数据和设备都通过一套相同的系统调用接口进行
  • 内核用C语言编写,移植能力很强
  • 进程创建迅速,独特的fork调用
  • 提供了简洁但是稳定的进程间通讯原语

1.2 unix和linux

  • linux克隆unix,但不是unix
  • linux借鉴了unix很多的设计,并且实现了 unix的api
  • linux没有直接使用unix的源代码,但完整表达了unix的设计目标并保证编程接口一致

2. 操作系统和内核简介

  • 内核一般包括: 中断服务程序:负责响应中断 调度程序:管理多进程,分配处理器时间 内存管理程序:管理内存空间 系统服务程序:包括网络,进程间通讯
  • 应用程序通过系统调用和内核通讯来运行
  • 应用程序通常调用库函数,库函数通过系统调用让内核带其完成各种任务
  • 内核对硬件设备的管理:硬件想要通讯时,发送异步信号去打断内核,内核通过中断号查找处理程序
  • linux内核开发的特定 不能链接标准c函数库。c库太大了,会影响大小和效率。不过大部分常用的c函数在内核中都有实现 没有内存保护机制,要注意非法访问内存地址 不要轻易使用浮点数,要人工保存和恢复浮点寄存器 栈空间很小且固定。32为机器为8kb,64为16kb 内核很容易产生竞争条件,注意同步和并发 注意可移植性

二. 进程管理

1. 基本概念

  • unix系统的两大抽象对象:进程,文件。
  • 进程是处于执行期的程序,linux通常也把进程叫做任务
  • 进程包括:代码段,数据段,打开的文件,挂起的信号,地址空间,线程等
  • 线程是进程中活动的执行对象
  • 每个线程拥有独立的程序计数器,进程栈和一组进程寄存器
  • 内核调度的对象是线程,而不是进程
  • linux的线程实现非常特别,并不特别区分线程和进程
  • 进程提供两种虚拟机制:虚拟处理器和虚拟内存
  • 同一个进程内的线程可以共享虚拟内存,但是有各自的虚拟处理器

2. 进程描述符及任务队列

2.1 基本概念

  • 内核把进程存放在叫做任务队列的双向循环链表中
  • 链表中每一项都是task_struct类型,称为进程描述符,包括一个进程的所有信息。路径:/include/linux/sched.h

2.2 进程描述符如何分配

  • linux通过slab分配其分配task_struct结构,这样能达到对象复用和缓存着色
  • 通过预先分配和重复使用task_struct,避免动态分配和释放带来的性能损耗,这也是为什么创建进程快的原因
  • task_struct放在内核栈的尾端,为了让寄存器少的硬件体系只通过栈指针就能算出位置,避免使用额外寄存器存储
  • slab分配器在内核栈的尾部创建新的struct thread_info,内部的task指向实际的task_struct。thread_info位置:<asm/thread_info.h>

2.3 进程描述符存放在哪

  • current宏可以查找当前正在运行进程的进程描述符
  • 这个宏的具体实现根据各自硬件体系结构有所不同
  • x86体系,通过栈尾部的thread_info结构的task指针找到进程描述符
  • 有的体系(IBM的RISC),分配一个专用的寄存器存放task_struct的地址

2.4 进程的状态

  • 进程描述符的state字段描述了进程当前的状态,每个进程都处于五种状态的一种 TASK_RUNNING:运行。进程是可执行的。 TASK_INTERRUPTIBLE:可中断。进程被阻塞,等待被唤醒 TASK_UNINTERRUPTIBLE:不可中断。收到信号不做任何响应。ps命令查看会显示D TASK_ZOMBIE:僵死。进程已经结束了,但是父进程还没有调用wait4系统调用 TASK_STOPPED:停止。进程停止执行
  • 状态变迁图

 

  • 设置当前进程:set_task_state(task,state)或set_current_state

2.5 进程上下文

  • 一般程序在用户空间执行,执行系统调用或触发异常时,进入内核空间,内核代表进程执行,并处于进程上下文中
  • 系统调用和异常处理是内核明确定义的接口,对内核的所有访问也只能通过这些接口
  • linux进程有明显的继承关系,所有的进程都是pid为1的init进程的后代
  • 系统中的每个进程必有一个父进程,每个进程可以拥有一个或多个子进程
  • 进程间关系存放在进程描述符中。task_struct中的parent变量,指向一个task_struct,存放父进程地址。children变量是一个链表,指向所有的子进程

3. 进程创建

3.1 基本概念

  • unix进程创建分为:fork和exec两步
  • fork通过拷贝当前进程创建子进程。子进程和父进程仅有很少差异:pid,ppid,某些资源和统计量
  • exec负责读取可执行文件并载入地址空间开始运行

3.2 写时拷贝(COW)

  • 传统的fork直接拷贝资源效率低下,linux使用写时拷贝(copy on write)技术提高效率
  • COW并不会复制整个地址空间,而是让父子进程以只读方式共享内存,数据的复制只有在写入时才进行

3.3 fork函数

  • linux通过clone()系统调用实现fork(), 这个调用通过参数标识(很多种类型)指明需要共享的资源
  • clone内部调用do_fork完成主要工作(kernel/fork.c)
  • do_fork内部调用copy_process,然后让进程运行
  • copy_process调用过程: 调用dup_task_struct为新进程创建内核栈,thread_info结构和task_struct,这些值与当前进程相同,此时描述符完全相同 检查系统拥有的进程数是否超过限制 将很多成员重置 设置状态为TASK_UNINTERRUPTIBLE保证不会被运行 调用copy_flags以更新task_struct的flags成员 调用get_pid获取新的pid 根据参数标识,拷贝或共享打开的文件,文件系统信息,信号处理函数,进程地址空间,命名空间等。一般情况下,这些资源是线程共享的 父子进程平分时间片 扫尾工作,并返回指向子进程的指针
  • 新创建的进程被唤醒并让其投入运行,一般优先子进程首先执行

3.4 vfork函数

  • 和fork功能相同,除了补考吧父进程的页表项
  • 通过向clone系统调用传递一个特殊标志进行的
  • 该函数的设计并不是很优良的

4. 线程在linux中的实现

4.1 liunx线程概述

  • 一组线程共享进程内的内存地址空间,打开的文件和其他资源
  • 线程机制支持并发程序设计技术,多处理器上保证真正的并行处理
  • linux实现线程的机制非常独特,从内核角度看,没有线程的概念
  • linux把所有线程都当做进程来实现,内核没有特别的调度算法或数据结构来表征线程,被视为一个使用某些共享资源的进程
  • 每个线程有自己的task_struct,就像一个普通的进程,这个进程和其他进程共享某些资源
  • 与其他系统(windows,solaris)实现差异巨大,这些系统内核专门提供线程的支持

4.2 linux线程创建

  • 线程的创建和普通进程创建类型,只不过调用clone时需要传递一些参数标志,指明需要共享的资源
  • 参数标志说明: CLONE_VM:父子进程共享地址空间 CLONE_SIGHAND:父子进程共享信号处理函数 CLONE_THREAD:父子进程放入相同线程组 CLONE_FS:父子进程共享文件系统信息 CLONE_FILES:共享打开的文件 ...

4.3 内核线程

  • 内核线程:独立运行在内核空间的标准进程
  • 和普通进程的区别:没有独立的地址空间,只能在内核空间运行
  • 创建只能由其他内核线程创建,函数为kernel_thread

4.4 进程终结

释放资源

  • 进程终结时,内核必须释放它所占有的资源,并通知父进程
  • 结束可以是正常,异常,还可以注册终止清理函数
  • 最终结束会调用do_exit(kenel/exit.c),完成的工作包括: 将task_struct的标志成员设置为PF_EXITING 如果进程会计功能开启&#x
  • 2
    点赞
  • 38
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值