linux:内核态和用户态、进程切换、进程间通信(IPC)方式及特点 协程和线程

本文详细介绍了操作系统中的内核态和用户态,以及如何从用户态切换到内核态,包括系统调用和异常处理。同时,阐述了线程与进程的区别,重点讨论了进程切换的过程和进程间通信的各种方法,如管道、信号量、消息队列和共享内存。此外,还对比了线程和协程在资源管理和切换效率上的差异。
摘要由CSDN通过智能技术生成

参考链接一
参考链接二
参考链接三
参考链接四
参考链接五

内核态和用户态

  • 内核态和用户态是CPU选线的两个等级Ring0和Ring3;
  • CPU处于内核态时,可以使用任何CPU指令集、访问一切硬件资源,例如进程切换、内存管理、文件系统管理等;
  • CPU处于用户态则会有访问限制。这样的好处是分而治之,用户进程不能随意修改操作系统内核空间的资源,且发生异常时可恢复,有保护的作用。
  • 现在的操作系统都采用了虚拟存储技术,对于32位操作系统,最大寻址空间位4G,linux操作系统将高1GB作为内核空间,将低3GB作为用户空间,每个进程都有这样一个虚拟内存,页表记录了虚拟内存到物理内存的映射关系。
  • 例如socket,客户端发送的请求或数据会先放入内核的缓冲区,用户态进程调用recvform()函数从内核拷贝

用户态切换到内核态

  1. 系统调用:用户态申请系统调用切换到内核态

    • Linux系统有一个系统调用的表,每一个注册过的系统调用都有专属的系统调用号;
    • Linux通过软中断完成从用户态到内核态的切换:
      • 在用户执行系统调用时会引发一个异常,这个异常执行int&0x80指令
      • int&0x80会导致系统从用户态切换到内核态,并执行128号异常(中断)
      • 而128号异常就是用来处理中断的,其实就是调用system_call()函数并根据系统调用号来判断调用哪个函数。用户进程会将系统调用号通过eax寄存器传入内核,内核调用函数。
      • 参数传递:系统调用号是通过eax寄存器传递给内核的,而对应的系统调用可能需要一些参数,ebx、ecx、edx、esi、edi这五个寄存器是用来传递系统调用参数的,如果出现参数大于五个的话,那就需要创建一个堆栈,用一个单独的寄存器来保存这个堆栈的地址,从而将参数传递到内核态
        在这里插入图片描述
  2. 异常:用户态发生异常,切换到内核态由相关程序处理异常
    当CPU在执行用户态程序时,发生了某些异常,就会触发当前进程切换到处理该异常的内核相关程序中,例如缺页异常(硬中断)

  3. 外围设备中断:若当前出于用户态,接到中断就会切换到内核态处理
    当外围设备完成用户态的请求时,就会使出于用户态的CPU停止运行,转为内核态执行对应的中断程序。

线程与进程的区别

  • “进程是资源分配的基本单位,线程资源调度的基本单位”,在我看来,区分进程和线程,就是为了更好的利用CPU,按上述所说,进程的切换涉及到虚拟内存的切换,切换的效率太低,所以可以创造出一个更快的切换方式。线程就是这样,多个线程共享一个进程里的资源(虚拟内存),不需要频繁切换进程所需的资源,那这样切换更快,可以让CPU闲置的时间更少一点,而进程就变成了一个容器。
  • 在Linux内核中,进程/线程都是用task_struct结构体保存他们的信息,而且创建进程/线程最终都是调用do_fork()函数,因此我们说linux内核并不区分进程或线程。当然,进程/线程还是有些许不同的:
    1. 进程/线程都有一个pid和tgid,pid各不相同,同一进程内所有线程的tgid相同,等于该进程的主线程pid
    2. long do_fork(unsigned long clone_flags, unsigned long stack_start, unsigned long stack_size, int __user *parent_tidptr, int __user *child_tidptr)

进程切换

  • 只有内核能挂起正在CPU上运行的进程,或者恢复之前被挂起的进程,进程切换的步骤:
    1. 保存处理机上下文,包括程序计数器、寄存器
    2. 更新PCB信息
    3. 将进程的PCB保存到就绪、阻塞队列
    4. 选择另一个进程、更新其PCB
    5. 更新内存的数据结构
    6. 恢复处理机上下文
  • 进程的阻塞
    进程在请求资源失败、等待某些操作完成时,系统自动执行阻塞原语(Block),因此进程的阻塞是主动行为,而且只有运行状态的进程能阻塞。进程阻塞不占用CPU资源。

进程间通信

通信方式特点
无名管道进程调用fork()函数,就能用于父子进程间通信,单向传输(半双工)
流管道双向传输(全双工),也只能在父子进程间使用
有名管道单向传输,可以在任意进程间使用
信号由用户、系统或进程通知某个目标进程事件的发生,传递信息少
信号量实际上是一个计数器,用于进程、线程间同步,常与共享内存配合使用,协调进程访问共享内存(临界资源)
消息队列消息队列存储在内核中,进程间可以通过共享消息队列完成通信,且不同于管道只能传输无格式字节流,它能传输格式化的数据,但需要CPU参与复制,不适合消息量大且操作频繁的场景
共享内存具有消息量大、无需复制的优点,通过将共享的内存缓冲区放到进程的虚拟内存来实现,但是要和其它消息传输机制(例如信号量)配合实现进程间同步
套接字(socket)C/S模式,通常用于不同计算机的进程间通信
  • 管道相对于通信双方就是一个文件,并且这个文件只存在于内核中,基于先进先出的方式,只能传送无格式字节流,C语言创建管道其实是创建了两个无名管道,实现双向通信。
  • 共享内存:最快的IPC方式。其实就是一块内存空间映射到A、B不同进程的虚拟内存空间。

协程和线程

线程和协程一样,共享堆,但不共享栈。

  1. 协程切换完全在用户态进行,线程需要权限切换
  2. 协程切换相比线程做的事情要少

协程切换只需要将寄存器的值改变就行,线程则需要系统调用到内核态进行,保存当前线程的私有栈数据和寄存器。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值