Java程序员需要掌握的计算机底层知识(三):进程、线程、纤程、中断
面試高頻問題
問:進程和線程有什么區(qū)別?
答:進程是一個程序運行起來的狀態(tài)(運行態(tài)),線程是一個進程中不同的執(zhí)行路徑(線程只是其中一個)。
更為專業(yè)的回答:進程是操作系統(tǒng)用來分配資源的基本單位,線程是操作系統(tǒng)用來執(zhí)行調(diào)度的基本單位。
進程、線程、纖程
1、進程
雙擊QQ.exe,操作系統(tǒng)通過IO把它從磁盤取出來,放進內(nèi)存中,形成一個進程。
再雙擊QQ.exe,又開啟一個進程…(同一個可以多次啟動,開啟多個進程)
進程最重要的資源,是它被分配的自己獨立的內(nèi)存空間。
程序開始執(zhí)行之后,首先有一個main主線程。之后可能會有其他的線程,線程調(diào)度執(zhí)行。
線程和進程的區(qū)別:
(標(biāo)準(zhǔn)回答)線程共享進程的內(nèi)存空間,沒有自己獨立的內(nèi)存空間。
操作系統(tǒng)的線程是怎么實現(xiàn)的?
不同的操作系統(tǒng)對于線程的實現(xiàn)是不同的。
可以讀一下《Linux內(nèi)核設(shè)計與實現(xiàn)》
在Linux中,進程就是一個fork,會從現(xiàn)有的進程里面開啟一個新的子進程。
在Linux里面,進程和線程沒有太大的區(qū)別,線程就是一個普通的進程,只不過和其他進程共享資源(內(nèi)存空間、全局數(shù)據(jù)等…)
在其他操作系統(tǒng)中(比如Windows,Solaris),有各自LWP的實現(xiàn),線程是輕量級進程。
協(xié)程(纖程)
JVM(HotSpot)和操作系統(tǒng)的線程是一一對應(yīng)的:JVM空間的一個線程,對應(yīng)操作系統(tǒng)中的一個線程,這是重量級線程。
操作系統(tǒng)可以開啟的線程是有限的(1萬個已經(jīng)很卡了),而纖程是用戶空間的,不需要與內(nèi)核打交道,輕量級,切換速度快,可以啟動幾萬個,甚至幾十萬個都沒有問題。
纖程
纖程可以被理解為:線程里的線程,在JVM層級用軟件進行內(nèi)部的線程協(xié)調(diào)。
纖程是被JVM管理,運行在用戶態(tài),操作和調(diào)度不經(jīng)過操作系統(tǒng)內(nèi)核。
關(guān)于纖程的實現(xiàn):纖程可以分配自己獨立的纖程棧,相當(dāng)于自己實現(xiàn)了一個小小的操作系統(tǒng)級別的調(diào)度程序。
纖程的優(yōu)勢
1、占有資源很少。操作系統(tǒng)啟動一個線程占用1M的內(nèi)存空間,而啟動一個纖程只需要4K空間
2、由于輕量級,切換簡單
3、由于輕量級,可以啟動幾十萬個纖程都沒有問題。
2020目前支持內(nèi)置纖程的語言
Kotlin Scala Go Python(+lib)
Java通過類庫可以支持:OpenJDK的loom項目正在做纖程的嘗試。目前 JDK 14 還沒有支持纖程,期待 JDK 15 吧…
利用Quaser庫(不成熟)
如果大規(guī)模應(yīng)用在生產(chǎn),還是有一些小bug存在的。
pom.xml
<?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>HelloFiber.java 線程版
耗時7s
HelloFiber2.java 纖程版
耗時3s
可再優(yōu)化:
- 目前是10000個Fiber,在1個JVM線程中執(zhí)行。
- 可以考慮將10000個計算任務(wù)分為10個線程,讓10個線程去執(zhí)行,每個線程執(zhí)行1000個。
這樣既充分利用了操作系統(tǒng)在內(nèi)核級別對于線程的調(diào)度,又充分利用了JVM在用戶空間的對于纖程的調(diào)度。
纖程的應(yīng)用場景
- 纖程 vs 線程池:什么時候用纖程,什么時候用線程池?
纖程應(yīng)用在很短的計算任務(wù),不需要和內(nèi)核打交道,并發(fā)量高的時候,適合使用纖程。
如果是讀文件之類的任務(wù),用線程比較好。
內(nèi)核線程(了解)
內(nèi)核線程是內(nèi)核獨用的線程,用來進行一些后臺操作,比如垃圾清理等。
進程創(chuàng)建和啟動
在Linux中,fork 底層調(diào)用的是 clone,是從當(dāng)前進程 clone 出一個新的進程來。
僵尸進程、孤兒進程
1、僵尸進程
在大多數(shù)情況下,少量僵尸進程的存在,對操作系統(tǒng)沒有太大影響,它基本不再占用任何資源了,它在內(nèi)存中唯一占用的資源就是PCB了。
僵尸進程的產(chǎn)生原因,有可能父進程是一個daemon進程,C程序開發(fā)人員在父進程沒有釋放子進程,這時會出現(xiàn)僵尸進程。
可以使用內(nèi)核中的wait函數(shù),釋放僵尸進程
僵尸進程c示例:
2、孤兒進程
孤兒進程產(chǎn)生的原因:子進程還活著,父進程掛了
孤兒進程和孤兒線程沒有太大區(qū)別,因為在Linux中,進程和線程沒有太大區(qū)別。
孤兒進程的影響:影響不大。
孤兒進程c示例:
#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()); // 打印爹的 id 號sleep(10); // 子進程睡 10 秒,此過程中父進程已經(jīng)主動退出了printf("parent ppid is %d\n", getppid()); // 換了個爹} else { // 這里是父進程printf("parent id is %d\n", getpid()); // 打印自己的 id 號sleep(5); // 睡 5 秒exit(0); // 父進程主動退出} }運行結(jié)果
進程號1457的進程,它的父親進程號是1
進程調(diào)度
Linux中特別靈活,有各種各樣的調(diào)度方案,可以寫內(nèi)核去自定義。
進程調(diào)度
(目前)Linux 2.6 內(nèi)核版本采用 CFS 調(diào)度策略:Completely Fair Scheduler 完全公平
CFS調(diào)度策略:按優(yōu)先級分配時間片的比例,記錄每個進程的執(zhí)行時間,如果有一個進程執(zhí)行時間不到他應(yīng)該分配的比例(比如給了你50%,你只用了10%),就優(yōu)先執(zhí)行這個進程作為補償。
進程調(diào)度基本概念
Linux 默認調(diào)度策略:
1、實時進程:先執(zhí)行
- 優(yōu)先級不一樣:使用FIFO策略 (First In First Out),優(yōu)先級最高的先執(zhí)行
- 優(yōu)先級一樣 :使用RR策略(Round Robin)輪詢
2、普通進程: CFS完全公平策略
中斷
外設(shè)的輸入隨時都有可能到達,cpu如何及時的知道并進行處理呢?
cpu提供中斷機制來滿足這種需要,當(dāng)cpu的內(nèi)部有需要處理的事情發(fā)生時,將產(chǎn)生中斷信息,引發(fā)中斷過程,這種中斷信息來自cpu內(nèi)部,還有一種中斷信息,來自于cpu外部,當(dāng)cpu外部有需要處理的事情發(fā)生的時候,比如說,外設(shè)的輸入到達,相關(guān)芯片將向cpu發(fā)出相應(yīng)的中斷信息。cpu在執(zhí)行完當(dāng)前指令后,可以檢測到發(fā)送過來的中斷信息,引發(fā)中斷過程,處理外設(shè)的輸入。
如何用硬件的電信號控制軟件的輸出的?
1、鍵盤按下后,CPU去查是哪種類型的中斷
2、如果這個中斷在中斷向量表中,CPU到內(nèi)存中的一個固定位置找到對應(yīng)的中斷處理程序,進行處理
- 上半場:內(nèi)核內(nèi)部處理
- 下半場:交給軟件處理
硬中斷
鍵盤、網(wǎng)卡、打印機、時鐘計時器的中斷,都屬于硬中斷。
軟中斷
軟件產(chǎn)生的中斷,做系統(tǒng)調(diào)用時,需要驚動內(nèi)核。
比如想要和硬盤打交道,需要調(diào)用內(nèi)核,讓內(nèi)核去做硬盤的訪問,這是軟中斷。
軟件給內(nèi)核一個 0x80中斷 信號,然后去調(diào)用200多個函數(shù)中的一個。
阿里面試題:解釋80中斷的執(zhí)行過程
int 0x80 是C語言的函數(shù),現(xiàn)在 intel 已經(jīng)直接移植到了硬件層面,sysenter原語是匯編語言實現(xiàn)的。
bx cx dx si di存儲著內(nèi)核函數(shù)的5個參數(shù)
比如,InputStream中的read()方法:
從匯編角度理解80軟中斷
搭建CentOS匯編環(huán)境
yum install nasm
編譯:nasm -f elf hello.asm -o hello.o(編譯成為二進制)
鏈接:ld -m elf_i386 -o hello hello.o(和其他引用到的類庫進行連接,才能生成可執(zhí)行文件)
一個程序的執(zhí)行過程,要么處于用戶態(tài),要么處于內(nèi)核態(tài)
總結(jié)
以上是生活随笔為你收集整理的Java程序员需要掌握的计算机底层知识(三):进程、线程、纤程、中断的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Java程序员需要掌握的计算机底层知识(
- 下一篇: java美元兑换,(Java实现) 美元