进程 线程 纤程(面试高频)
面试高频:进程和线程有什么区别?
不专业:进程就是一个程序运行起来的状态,线程是一个进程中的不同的执行路径。
专业:进程是OS分配资源的基本单位,线程是执行调度的基本单位。
分配资源最重要的是:独立的内存空间。线程调度执行(线程共享进程的内存空间,没有自己独立的内存空间)
进程
线程
纤程
纤程fiber:用户态的线程,线程中的线程,切换和调度不需要经过OS
优势:
1:占有资源很少 OS : 线程需要1M Fiber只需要4K空间
2:切换比较简单
3:启动很多个10W+
2020-3-22支持内置纤程的语言:Kotlin Scala Go Python(lib)… Java? (open jdk : loom)
Hotspot目前的线程,每次申请都需要和操作系统打交道,通过内核申请,被称为重量级线程
而纤程的作用是直接和JVM的线程打交道,一个JVM线程可以申请多个纤程,效率会更快
纤程的实现
Java中对于纤程的支持:没有内置,盼望内置
利用Quaser库(不成熟)
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>mashibing.com</groupId>
<artifactId>HelloFiber</artifactId>
<version>1.0-SNAPSHOT</version>
<dependencies>
<!-- https://mvnrepository.com/artifact/co.paralleluniverse/quasar-core -->
<dependency>
<groupId>co.paralleluniverse</groupId>
<artifactId>quasar-core</artifactId>
<version>0.8.0</version>
</dependency>
</dependencies>
</project>
import co.paralleluniverse.fibers.Fiber;
import co.paralleluniverse.fibers.SuspendExecution;
import co.paralleluniverse.strands.SuspendableRunnable;
public class HelloFiber {
public static void main(String[] args) throws Exception {
long start = System.currentTimeMillis();
Runnable r = new Runnable() {
@Override
public void run() {
calc();
}
};
int size = 10000;
Thread[] threads = new Thread[size];
for (int i = 0; i < threads.length; i++) {
threads[i] = new Thread(r);
}
for (int i = 0; i < threads.length; i++) {
threads[i].start();
}
for (int i = 0; i < threads.length; i++) {
threads[i].join();
}
long end = System.currentTimeMillis();
System.out.println(end - start);
}
static void calc() {
int result = 0;
for (int m = 0; m < 10000; m++) {
for (int i = 0; i < 200; i++) result += i;
}
}
}
import co.paralleluniverse.fibers.Fiber;
import co.paralleluniverse.fibers.SuspendExecution;
import co.paralleluniverse.strands.SuspendableRunnable;
public class HelloFiber2 {
public static void main(String[] args) throws Exception {
long start = System.currentTimeMillis();
int size = 10000;
Fiber<Void>[] fibers = new Fiber[size];
for (int i = 0; i < fibers.length; i++) {
fibers[i] = new Fiber<Void>(new SuspendableRunnable() {
public void run() throws SuspendExecution, InterruptedException {
calc();
}
});
}
for (int i = 0; i < fibers.length; i++) {
fibers[i].start();
}
for (int i = 0; i < fibers.length; i++) {
fibers[i].join();
}
long end = System.currentTimeMillis();
System.out.println(end - start);
}
static void calc() {
int result = 0;
for (int m = 0; m < 10000; m++) {
for (int i = 0; i < 200; i++) result += i;
}
}
}
作业:目前是10000个Fiber -> 1个JVM线程,想办法提高效率,比如10000Fiber -> 10份 -> 10Threads
纤程的应用场景
纤程 vs 线程池:很短的计算任务,不需要和内核打交道,并发量高!
内核线程(了解即可)
内核在启动之后经常需要做一些后台操作,这些由Kernel Thread来完成,只在内核空间运行。
比如 计时、定期清理垃圾
进程的创建和启动
系统函数:系统对外提供的接口
僵尸进程 孤儿进程
僵尸进程实验
linux 中 ps命令出来的进程带有< defunct >则为僵尸进程
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <assert.h>
#include <sys/types.h>
int main() {
pid_t pid = fork();
if (0 == pid) {
printf("child id is %d\n", getpid());
printf("parent id is %d\n", getppid());
} else {
while(1) {
}
}
}
孤儿进程实验
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <assert.h>
#include <sys/types.h>
int main() {
pid_t pid = fork();
if (0 == pid) {
printf("child ppid is %d\n", getppid());
sleep(10);
printf("parent ppid is %d\n", getppid());
} else {
printf("parent id is %d\n", getpid());
sleep(5);
exit(0);
}
}
进程调度
linux中每个进程都有自己专属的调度方案
早期的操作系统是单任务(独占CPU),后面逐渐变为多任务(分时占用CPU)
进程调度原则:最大限度的压榨CPU资源
进程调度基本概念
- 进程类型
- IO密集型:大部分时间用于等待IO
- CPU密集型:大部分时间用于闷头计算
- 进程优先级
- 实时进程 > 普通进程 (0 - 99)
- 普通进程nice值(-20 - 19)
- 时间分配
- linux采用按优先级的CPU时间比
- 其他系统多采用按优先级的时间片
- eg.两个app同时运行
- 一个文本处理程序
- 一个影视后期程序
进程调度算法
多任务情况下:
- 非抢占式(cooperative multitasking) : 除非进程主动让出CPU(yielding),否则将一直运行
- 抢占式(preemptive multitasking) :由
进程调度器
强制开始或暂停(抢占)某一进程的执行
Linux的调度策略(选修)
linux2.5 经典Unix O(1)调度策略,偏向服务器,但对交互不友好
linux2.6.23 采用CFS完全公平调度算法Completety Fair Scheduler
CFS:按优先级分配时间片的比例,记录每个进程的执行时间,如果有一个进程执行时间不到他应该分配的比例,优先执行
默认调度策略:
实时进程 (急诊) 优先级分高低 - FIFO (First In First Out),优先级一样 - RR(Round Robin)
普通进程: CFS
中断
硬件跟操作系统内核打交道的一种机制
软中断(0x80中断信号) == 系统调用
系统调用:int 0x80(老的,在软件层面支持) 或者 sysenter原语(新的,直接在硬件层面支持)
通过ax寄存器填入调用号(对应不同函数)
参数通过bx cx dx si di传入内核
返回值通过ax返回
例子:java读网络 –> jvm read() –> c库read() - > 内核空间 -> system_call() (系统调用处理程序)-> sys_read()
中断处理机制的实现细节
从汇编角度理解软中断
搭建汇编环境
yum install nasm
;hello.asm
;write(int fd, const void *buffer, size_t nbytes)
;fd 文件描述符 file descriptor - linux下一切皆文件
section data
msg db "Hello", 0xA
len equ $ - msg
section .text
global _start
_start:
mov edx, len
mov ecx, msg
mov ebx, 1 ;文件描述符1 std_out
mov eax, 4 ;write函数系统调用号 4
int 0x80
mov ebx, 0
mov eax, 1 ;exit函数系统调用号
int 0x80
编译:nasm -f elf hello.asm -o hello.o
链接:ld -m elf_i386 -o hello hello.o
一个程序的执行过程,要么处于用户态,要么处于内核态