32位系统中,一个linux进程最大可以申请4GB地址空间,其中3g被划分为用户空间,1g被划分为内核空间。**内核空间的数据是进程间共享的(所有进程的内核空间映射的内存地址是一样的)。**
用户态与内核态
:::success
- 当一个进程在执行用户自己的代码时处于用户运行态(用户态),此时特权级最低,为3级,是普通的用户进程运行的特权级,大部分用户直接面对的程序都是运行在用户态。
- 当一个进程因为系统调用陷入内核代码中执行时处于内核运行态(内核态),此时特权级最高,为0级。执行的内核代码会使用当前进程的内核栈,每个进程都有自己的内核栈。
- 用户态只能受限的访问内存,且不允许访问外围设备,不容许访问内核空间,占用CPU的能力被剥夺,CPU资源可以被其他程序获取; :::
用户运行一个程序,该程序创建的进程开始时运行自己的代码,处于用户态。
如果要执行文件操作、网络数据发送等操作必须通过write、send等系统调用,这些系统调用会调用内核的代码。进程会切换到Ring0,然后进入3G-4G中的内核地址空间去执行内核代码来完成相应的操作。内核态的进程执行完后又会切换到Ring3,回到用户态。这样,用户态的程序就不能随意操作内核地址空间,具有一定的安全保护作用。这说的保护模式是指通过内存页表操作等机制,保证进程间的地址空间不会互相冲突,一个进程的操作不会修改另一个进程地址空间中的数据。
用户态到内核态的切换
以下的三种情况会导致用户态到内核态的切换
- 系统调用** (用户进程主动发起)**
- 异常(被动),当CPU在执行运行在用户态下的程序时,发生了某些事先不可知的异常,这时会触发由当前运行进程切换到处理异常的内核相关程序中,也就转到了内核态
- 外围设备的中断信号(被动)
用户态和内核态的切换涉及的CPU上下文切换
第一次切换:
CPU寄存器里原来用户态的指令位置,需要先保存起来。接着,为了执行内核态代码,CPU寄存器需要更新为内核态指令的新位置。最后才是跳转到内核态运行内核任务。
第二次切换:
而系统调用结束后,CPU寄存器需要恢复原来保存的用户态,然后再切换到用户空间,继续运行进程。所以,一次系统调用的过程,其实是发生了两次CPU上下文切换。
一个java的线程在运行的时候是内核态还是用户态?
其实站在java程序员的角度只需要关注系统调用,因为系统调用可以认为是用户进程主动发起的,比如调用线程的park()方法会对应到一个os的一个函数,从而使当前线程进入了内核态;再比如遇到synchronized关键字如果是重量锁则会调用pthread_mutex_lock()这样我们的线程也会切换到内核态;当执行完系统调用切换到用户态;

