以下针对java来说明

1. 进程 线程 纤程

在这里插入图片描述

进程和线程区别

进程就是一个程序运行起来的状态,线程是一个进程中的不同的执行路径。
进程是OS分配资源的基本单位,线程是执行调度的基本单位。分配资源最重要的是:独立的内存空间,线程调度执行(线程共享进程的内存空间,没有自己独立的内存空间)

线程在Linux中的实现

《Linux内核设计与实现》第三版28页
线程在Linux就是一个普通的进程,只不过和其他进程共享资源(内存空间,全局数据等)

其他系统都有各自所谓的LWP的实现 Light Weight Process(轻量级进程)

纤程

在这里插入图片描述
即:用户空间的线程

为什么需要纤程:
java启动线程,在操作系统级别,就是启一个LWP。这是重量级线程。因为java启动线程需要向操作系统申请资源,和操作系统内核打交道,需要系统调用。
而纤程是线程中的线程,对应图最上面蓝色框,在用户空间,不需要向操作系统申请。
纤程处于线程内部,非常轻量级,可以在线程中快速切换。JVM自己管理,自己实现调度,自己切换,与操作系统无关。

纤程优势:

  1. 占有资源很少 OS : 线程:1M vs Fiber:4K
  2. 切换比较简单
  3. 启动很多个10W+

纤程的应用场景:
很短的计算任务,不需要和内核打交道,并发量高

2. 进程

Linux中也称为task,是系统分配资源的基本单位
资源包括:独立的地址空间,内核数据结构(进程描述符…),全局变量, 数据段…
Linux进程描述符:PCB (Process Control Block),用于Linux的进程管理(线程有他的PCB)

僵尸进程

ps -ef |grep defunct (defunct表示无用的僵尸进程)

父进程产生子进程后,会维护子进程的PCB结构,子进程退出后,由父进程释放,如果父进程没有释放,那么子进程会成为一个僵尸进程(defunct)

孤儿进程

子进程结束之前,父进程已经退出,就会产生孤儿进程,孤儿进程会成为Init进程的子进程,由1号进程维护 (在图形化Linux中是1457号线程)

调度策略

早期Linux 2.5 内核用的是Unix O(1)调度策略,按固定的时间片给程序

Linux内核2.6.23 采用CFS调度策略:Completely Fair Scheduler
按优先级分配时间片的比例,记录每个进程的执行时间,如果有一个进程执行时间不到他应该分配的比例,优先执行。 (比如有的程序开始没做什么就会让出给其他程序,当开始需要做的时候,优先执行)

默认调度策略:
实时进程 > 普通进程
实时进程优先级分高低 - FIFO (First In First Out),优先级一样的时候 - RR轮询(Round Robin)
普通进程: CFS

其中等级最高的是FIFO,这种进程除非自己让出CPU否则Linux会一直执行它,除非更高级的FIFO和RR抢占它
RR只是这种这种进程中是同级别FIFO中的平均分配 ---- 比如FIFO中有两个都是90优先级的进程,这时候按照RR执行。
只有实时进程主动让出,或者执行完毕后,普通进程才有机会运行。

3. 中断

硬中断

硬件和操作系统通信的一种机制
在这里插入图片描述
执行过程:
键盘 -----中断控制器(通知cpu一个中断信号来了)--------cpu(在内存中固定位置找处理程序)-------执行程序(查中断向量表,把中断信号给kernel)-------kernel(根据中断信号找程序)----------中断处理程序(已经写好的一堆处理程序,有处理键盘的,有处理打印机的)-----------------先由OS内核处理(上半场)------------再由应用程序处理(下半场)

软中断(80中断)

中断向量表特殊的符号
系统调用:int 0x80 (INT是用于x86处理器的汇编指令) 或者 sysenter原语(现在的cpu在硬件级别直接支持,汇编码)

通过ax寄存器填入调用号(比如1代表exit函数,2代表fork函数)
参数是通过bx cx dx si di传入内核
返回值通过ax返回

系统调用实现的hello world代码:

;以下例子主要调用了内核空间的write函数 
;write(int fd, const void *buffer, size_t nbytes) 
;fd文件描述符,buffer要写的内容,nbytes内容的长度

.section .data ;数据段声明
msg:
        .ascii "Hello world!\n"
.section .text ;代码段声明
.globl _start ;指定入口函数
_start: ;在屏幕上显示一个字符串
        movl $4, %eax ;系统调用sys_write
        movl $1, %ebx ;文件描述符1,std_out
        movl $msg, %ecx
        movl $13, %edx
        int $0x80
        movl $1, %eax ;系统调用sys_exit
        movl $0, %ebx
        int $0x80

系统调用是通过int 0x80来实现的,eax寄存器中为调用的功能号,ebx、ecx、edx、esi等等寄存器则依次为参数,从 /usr/include/asm/unistd.h中可以看到exit的功能号_NR_exit为1,write(_NR_write)功能号为4,因此第一个int 0x80调用之前eax寄存器值为4,ebx为文件描述符,stdout的文件描述符为1,ecx则为buffer的内存地址,edx为buffer长度。第二个int0x80之前eax为1表示调用exit,ebx为0表示传入参数0。

注:eax 是32位的,ax是16位的

java中例子
java读网络 – jvm read() – c库read() - > 内核空间 -> system_call() (系统调用处理程序)-> sys_read()

Logo

更多推荐