日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程语言 > java >内容正文

java

Java程序员需要掌握的计算机底层知识(三):进程、线程、纤程、中断

發(fā)布時間:2024/2/28 java 28 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Java程序员需要掌握的计算机底层知识(三):进程、线程、纤程、中断 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

面試高頻問題

:進程和線程有什么區(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

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() {@Overridepublic 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;}} }

HelloFiber2.java 纖程版
耗時3s

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;}} }

可再優(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示例:

#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(); // 從 A 進程 fork 出來一個子進程 B ?if (0 == pid) { // 這段是在 fork 的子進程執(zhí)行的 Bprintf("child id is %d\n", getpid());printf("parent id is %d\n", getppid()); // main 進程的 id} else { // 這段是執(zhí)行的進程 Awhile(1) {}} }

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

;hello.asm ;write(int fd, const void *buffer, size_t nbytes) 調(diào)用了內(nèi)核空間的write函數(shù) ;fd 文件描述符 file descriptor - linux下一切皆文件,網(wǎng)絡(luò)、硬盤、內(nèi)存、打印機、屏幕都可以對應(yīng)到各自的文件描述符 ? section datamsg db "Hello", 0xA ;定義一個字符串hello 0xA是換行l(wèi)en equ $ - msg ? section .text global _start _start: ?mov edx, lenmov ecx, msgmov ebx, 1 ;文件描述符1 表示std_out標(biāo)準(zhǔn)輸出mov eax, 4 ;write函數(shù)系統(tǒng)調(diào)用號 4int 0x80 ;對外輸出 ?mov ebx, 0mov eax, 1 ;exit函數(shù)系統(tǒng)調(diào)用號int 0x80

編譯: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)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。