linux多线程_Java+Linux,深入内核源码讲解多线程之进程
之前寫了兩篇文章,都是針對Linux這個系統(tǒng)的,為什么?我為什么這么喜歡寫這個系統(tǒng)的知識,可能就是為了今天的內(nèi)容多線程系列,現(xiàn)在多線程不是一個面試重點(diǎn) 啊,那如果你能深入系統(tǒng)內(nèi)核回答這個知識點(diǎn),面試官會怎么想?你會不會占據(jù)面試的主動權(quán)(我不會說今天被一個面試者驚艷到了的)今天,我就開始一個系列的內(nèi)容,多線程--高并發(fā),深入的給大家講解,我就不信講不明白這么個小東西,有問題的地方希望大家能夠指出,謝謝,大家一起成長
今天我們將第一個知識點(diǎn):進(jìn)程
Linux 內(nèi)核如何描述一個進(jìn)程?
1. Linux 的進(jìn)程
進(jìn)程的術(shù)語是 process,是 Linux 最基礎(chǔ)的抽象,另一個基礎(chǔ)抽象是文件。
最簡單的理解,進(jìn)程就是執(zhí)行中 (executing, 不等于running) 的程序。
更準(zhǔn)確一點(diǎn)的理解,進(jìn)程包括執(zhí)行中的程序以及相關(guān)的資源 (包括cpu狀態(tài)、打開的文件、掛起的信號、tty、內(nèi)存地址空間等)。
一種簡潔的說法:進(jìn)程 = n*執(zhí)行流 + 資源,n>=1。
Linux 進(jìn)程的特點(diǎn):
2. Linux 的進(jìn)程描述符
2.1 task_struct
內(nèi)核里,通過 task_struct 結(jié)構(gòu)體來描述一個進(jìn)程,稱為進(jìn)程描述符 (process descriptor),它保存著支撐一個進(jìn)程正常運(yùn)行的所有信息。
每一個進(jìn)程,即便是輕量級進(jìn)程(即線程),都有1個 task_struct。
?(include\linux)??struct?task_struct?{??struct?thread_info?thread_info;??volatile?long?state;??void?*stack;??[...]??struct?mm_struct?*mm;??[...]??pid_t?pid;??[...]??struct?task_struct?*parent;??[...]??char?comm[TASK_COMM_LEN];??[...]??struct?files_struct?*files;??[...]??struct?signal_struct?*signal;??}?這是一個龐大的結(jié)構(gòu)體,不僅有許多進(jìn)程相關(guān)的基礎(chǔ)字段,還有許多指向其他數(shù)據(jù)結(jié)構(gòu)的指針。
它包含的字段能完整地描述一個正在執(zhí)行的程序,包括 cpu 狀態(tài)、打開的文件、地址空間、掛起的信號、進(jìn)程狀態(tài)等。
作為初學(xué)者,先簡單地了解部分字段就好::
- struct thread_info thread_info: 進(jìn)程底層信息,平臺相關(guān),下面會詳細(xì)描述。
- long state: 進(jìn)程當(dāng)前的狀態(tài),下面是幾個比較重要的進(jìn)程狀態(tài)以及它們之間的轉(zhuǎn)換流程。
- void *stack: 指向進(jìn)程內(nèi)核棧,下面會解釋。
- struct mm_struct *mm: 與進(jìn)程地址空間相關(guān)的信息都保存在一個叫內(nèi)存描述符 (memory descriptor) 的結(jié)構(gòu)體 (mm_struct) 中。
pid_t pid: 進(jìn)程標(biāo)識符,本質(zhì)就是一個數(shù)字,是用戶空間引用進(jìn)程的唯一標(biāo)識。
struct?task_struct?*parent:?父進(jìn)程的?task_struct。??char?comm[TASK_COMM_LEN]:?進(jìn)程的名稱。??struct?files_struct *files:?打開的文件表。??struct?signal_struct?*signal:?信號處理相關(guān)。?其他字段,等到有需要的時候再回過頭來學(xué)習(xí)。
當(dāng)發(fā)生系統(tǒng)調(diào)用或者進(jìn)程切換時,內(nèi)核如何找到 task_struct ?
對于 ARM 架構(gòu),答案是:通過內(nèi)核棧 (kernel mode stack)。
為什么要有內(nèi)核棧?
因?yàn)閮?nèi)核是可重入的,在內(nèi)核中會有多條與不同進(jìn)程相關(guān)聯(lián)的執(zhí)行路徑。因此不同的進(jìn)程處于內(nèi)核態(tài)時,都需要有自己私有的進(jìn)程內(nèi)核棧 (process kernel stack)。
當(dāng)進(jìn)程從用戶態(tài)切換到內(nèi)核態(tài)時,所使用的棧會從用戶棧切換到內(nèi)核棧。
至于是如何切換的,關(guān)鍵詞是系統(tǒng)調(diào)用,這不是本文關(guān)注的重點(diǎn),先放一邊,學(xué)習(xí)內(nèi)核要懂得恰當(dāng)?shù)臅r候忽略細(xì)節(jié)。
當(dāng)發(fā)生進(jìn)程切換時,也會切換到目標(biāo)進(jìn)程的內(nèi)核棧。
同上,關(guān)鍵詞是硬件上下文切換 (hardware context switch),忽略具體實(shí)現(xiàn)。
無論何時,只要進(jìn)程處于內(nèi)核態(tài),就會有內(nèi)核棧可以使用,否則系統(tǒng)就離崩潰不遠(yuǎn)了。
ARM 架構(gòu)的內(nèi)核棧和 task_struct 的關(guān)系如下:
內(nèi)核棧的長度是 THREAD_SIZE,對于 ARM 架構(gòu),一般是 2 個頁框的大小,即 8KB。
內(nèi)核將一個較小的數(shù)據(jù)結(jié)構(gòu) thread_info 放在內(nèi)核棧的底部,它負(fù)責(zé)將內(nèi)核棧和 task_struct 串聯(lián)起來。thread_info 是平臺相關(guān)的,在 ARM 架構(gòu)中的定義如下:
//??(arch\arm\include\asm)??struct?thread_info?{??unsigned?long?flags;?/*?low?level?flags?*/??int?preempt_count;?/*?0?=>?preemptable,?<0?=>?bug?*/??mm_segment_t?addr_limit;?/*?address?limit?*/??struct?task_struct?*task;?/*?main?task?structure?*/??[...]??struct?cpu_context_save?cpu_context;?/*?cpu?context?*/??[...]??};?thread_info 保存了一個進(jìn)程能被調(diào)度執(zhí)行的最底層信息(low level task data),例如struct cpu_context_save cpu_context 會在進(jìn)程切換時用來保存/恢復(fù)寄存器上下文。
內(nèi)核通過內(nèi)核棧的棧指針可以快速地拿到 thread_info:
//??(include\linux)??static?inline?struct?thread_info?*current_thread_info(void)??{??//?current_stack_pointer?是當(dāng)前進(jìn)程內(nèi)核棧的棧指針??return?(struct?thread_info?*)??(current_stack_pointer?&?~(THREAD_SIZE?-?1));??}??然后通過?thread_info?找到?task_struct:??//?current.h?(include\asm-generic)??#define?current?(current_thread_info()->task)?內(nèi)核里通過 current 宏可以獲得當(dāng)前進(jìn)程的 task_struct。
2.3 task_struct 的分配和初始化
當(dāng)上層應(yīng)用使用 fork() 創(chuàng)建進(jìn)程時,內(nèi)核會新建一個 task_struct。
進(jìn)程的創(chuàng)建是個復(fù)雜的工作,可以延伸出無數(shù)的細(xì)節(jié)。這里我們只是簡單地了解一下 task_struct 的分配和部分初始化的流程。
fork() 在內(nèi)核里的核心流程:
dup_task_struct() 做了什么?
至于設(shè)置內(nèi)核棧里做了什么,涉及到了進(jìn)程的創(chuàng)建與切換,不在本文的關(guān)注范圍內(nèi),以后再研究了。
3. 實(shí)驗(yàn):打印 task_struct / thread_info / kernel mode stack
實(shí)驗(yàn)?zāi)康?#xff1a;
- 梳理 task_struct / thread_info / kernel mode stack 的關(guān)系。
實(shí)驗(yàn)代碼:
實(shí)驗(yàn)代碼:?#include?<linux/?#include?<linux/?#include?<linux/>??static?void?print_task_info(struct?task_struct?*task)?{?????printk(KERN_NOTICE?"%10s?%5d?task_struct?(%p)?/?stack(%p~%p)?/?thread_info->task?(%p)",?????????task->comm,?????????task->pid,?????????task,?????????task->stack,?????????((unsigned?long?*)task->stack)?+?THREAD_SIZE,?????????task_thread_info(task)->task);?}??static?int?__init?task_init(void)?{?????struct?task_struct?*task?=?current;??????printk(KERN_INFO?"task?module?init\n");??????print_task_info(task);?????do?{?????????task?=?task->parent;?????????print_task_info(task);?????}?while?(task->pid?!=?0);??????return?0;?}?module_init(task_init);??static?void?__exit?task_exit(void)?{?????printk(KERN_INFO?"task?module?exit\n?");?}?module_exit(task_exit);?運(yùn)行效果:
task?module?init?????insmod??3123?task_struct?(edb42580)?/?stack(ed46c000~ed474000)?/?thread_info->task?(edb42580)???????bash??2393?task_struct?(eda13e80)?/?stack(c9dda000~c9de2000)?/?thread_info->task?(eda13e80)???????sshd??2255?task_struct?(ee5c9f40)?/?stack(c9d2e000~c9d36000)?/?thread_info->task?(ee5c9f40)???????sshd???543?task_struct?(ef15f080)?/?stack(ee554000~ee55c000)?/?thread_info->task?(ef15f080)????systemd?????1?task_struct?(ef058000)?/?stack(ef04c000~ef054000)?/?thread_info->task?(ef058000)?在程序里,我們通過 task_struct 找到 stack,然后通過 stack 找到 thread_info,最后又通過 thread_info->task 找到 task_struct。
到這里,不知道你對進(jìn)程的概念是不是有了一個清晰的理解
但是上面是通過Linux進(jìn)行了線程的展示,在日常的工作中,代碼的實(shí)現(xiàn)和編寫我們還是以Java為主,那我們來看一下Java進(jìn)程
進(jìn)程的創(chuàng)建
Java提供了兩種方法用來啟動進(jìn)程或其它程序:
- 使用Runtime的exec()方法
- 使用ProcessBuilder的start()方法
ProcessBuilder
ProcessBuilder類是J2SE 1.5在中新添加的一個新類,此類用于創(chuàng)建操作系統(tǒng)進(jìn)程,它提供一種啟動和管理進(jìn)程(也就是應(yīng)用程序)的方法。在J2SE 1.5之前,都是由Process類處來實(shí)現(xiàn)進(jìn)程的控制管理。
每個 ProcessBuilder 實(shí)例管理一個進(jìn)程屬性集。start() 方法利用這些屬性創(chuàng)建一個新的 Process 實(shí)例。start() 方法可以從同一實(shí)例重復(fù)調(diào)用,以利用相同的或相關(guān)的屬性創(chuàng)建新的子進(jìn)程。
每個進(jìn)程生成器管理這些進(jìn)程屬性:
- 命令 是一個字符串列表,它表示要調(diào)用的外部程序文件及其參數(shù)(如果有)。在此,表示有效的操作系統(tǒng)命令的字符串列表是依賴于系統(tǒng)的。例如,每一個總體變量,通常都要成為此列表中的元素,但有一些操作系統(tǒng),希望程序能自己標(biāo)記命令行字符串——在這種系統(tǒng)中,Java 實(shí)現(xiàn)可能需要命令確切地包含這兩個元素。
- 環(huán)境 是從變量 到值 的依賴于系統(tǒng)的映射。初始值是當(dāng)前進(jìn)程環(huán)境的一個副本(請參閱 ())。
- 工作目錄。默認(rèn)值是當(dāng)前進(jìn)程的當(dāng)前工作目錄,通常根據(jù)系統(tǒng)屬性 來命名。
- redirectErrorStream 屬性。最初,此屬性為 false,意思是子進(jìn)程的標(biāo)準(zhǔn)輸出和錯誤輸出被發(fā)送給兩個獨(dú)立的流,這些流可以通過 () 和 () 方法來訪問。如果將值設(shè)置為 true,標(biāo)準(zhǔn)錯誤將與標(biāo)準(zhǔn)輸出合并。這使得關(guān)聯(lián)錯誤消息和相應(yīng)的輸出變得更容易。在此情況下,合并的數(shù)據(jù)可從 () 返回的流讀取,而從 () 返回的流讀取將直接到達(dá)文件尾。
修改進(jìn)程構(gòu)建器的屬性將影響后續(xù)由該對象的 start() 方法啟動的進(jìn)程,但從不會影響以前啟動的進(jìn)程或 Java 自身的進(jìn)程。大多數(shù)錯誤檢查由 start() 方法執(zhí)行。可以修改對象的狀態(tài),但這樣 start() 將會失敗。例如,將命令屬性設(shè)置為一個空列表將不會拋出異常,除非包含了 start()。
注意,此類不是同步的。如果多個線程同時訪問一個 ProcessBuilder,而其中至少一個線程從結(jié)構(gòu)上修改了其中一個屬性,它必須 保持外部同步。
構(gòu)造方法摘要
ProcessBuilder(List?command)??利用指定的操作系統(tǒng)程序和參數(shù)構(gòu)造一個進(jìn)程生成器。??ProcessBuilder(String...?command)??利用指定的操作系統(tǒng)程序和參數(shù)構(gòu)造一個進(jìn)程生成器。?方法摘要
List?command()??返回此進(jìn)程生成器的操作系統(tǒng)程序和參數(shù)。??ProcessBuilder?command(List?command)??設(shè)置此進(jìn)程生成器的操作系統(tǒng)程序和參數(shù)。??ProcessBuilder?command(String...?command)??設(shè)置此進(jìn)程生成器的操作系統(tǒng)程序和參數(shù)。??File?directory()??返回此進(jìn)程生成器的工作目錄。??ProcessBuilder?directory(File?directory)??設(shè)置此進(jìn)程生成器的工作目錄。??Map?environment()??返回此進(jìn)程生成器環(huán)境的字符串映射視圖。??boolean?redirectErrorStream()??通知進(jìn)程生成器是否合并標(biāo)準(zhǔn)錯誤和標(biāo)準(zhǔn)輸出。??ProcessBuilder?redirectErrorStream(boolean?redirectErrorStream)??設(shè)置此進(jìn)程生成器的?redirectErrorStream?屬性。??Process?start()??使用此進(jìn)程生成器的屬性啟動一個新進(jìn)程。?1.2 Runtime
每個 Java 應(yīng)用程序都有一個 Runtime 類實(shí)例,使應(yīng)用程序能夠與其運(yùn)行的環(huán)境相連接。可以通過 getRuntime 方法獲取當(dāng)前運(yùn)行時。
應(yīng)用程序不能創(chuàng)建自己的 Runtime 類實(shí)例。但可以通過 getRuntime 方法獲取當(dāng)前Runtime運(yùn)行時對象的引用。一旦得到了一個當(dāng)前的Runtime對象的引用,就可以調(diào)用Runtime對象的方法去控制Java虛擬機(jī)的狀態(tài)和行為。
Java代碼 收藏代碼
void?addShutdownHook(Thread?hook)??注冊新的虛擬機(jī)來關(guān)閉掛鉤。??int?availableProcessors()??向?Java?虛擬機(jī)返回可用處理器的數(shù)目。??Process?exec(String?command)??在單獨(dú)的進(jìn)程中執(zhí)行指定的字符串命令。??Process?exec(String[]?cmdarray)??在單獨(dú)的進(jìn)程中執(zhí)行指定命令和變量。??Process?exec(String[]?cmdarray,?String[]?envp)??在指定環(huán)境的獨(dú)立進(jìn)程中執(zhí)行指定命令和變量。??Process?exec(String[]?cmdarray,?String[]?envp,?File?dir)??在指定環(huán)境和工作目錄的獨(dú)立進(jìn)程中執(zhí)行指定的命令和變量。??Process?exec(String?command,?String[]?envp)??在指定環(huán)境的單獨(dú)進(jìn)程中執(zhí)行指定的字符串命令。??Process?exec(String?command,?String[]?envp,?File?dir)??在有指定環(huán)境和工作目錄的獨(dú)立進(jìn)程中執(zhí)行指定的字符串命令。??void?exit(int?status)??通過啟動虛擬機(jī)的關(guān)閉序列,終止當(dāng)前正在運(yùn)行的?Java?虛擬機(jī)。??long?freeMemory()??返回?Java?虛擬機(jī)中的空閑內(nèi)存量。??void?gc()??運(yùn)行垃圾回收器。??InputStream?getLocalizedInputStream(InputStream?in)??已過時。?從?JDK??開始,將本地編碼字節(jié)流轉(zhuǎn)換為?Unicode?字符流的首選方法是使用?InputStreamReader?和?BufferedReader?類。??OutputStream?getLocalizedOutputStream(OutputStream?out)??已過時。?從?JDK??開始,將?Unicode?字符流轉(zhuǎn)換為本地編碼字節(jié)流的首選方法是使用?OutputStreamWriter、BufferedWriter?和?PrintWriter?類。??static?Runtime?getRuntime()??返回與當(dāng)前?Java?應(yīng)用程序相關(guān)的運(yùn)行時對象。??void?halt(int?status)??強(qiáng)行終止目前正在運(yùn)行的?Java?虛擬機(jī)。??void?load(String?filename)??加載作為動態(tài)庫的指定文件名。??void?loadLibrary(String?libname)??加載具有指定庫名的動態(tài)庫。??long?maxMemory()??返回?Java?虛擬機(jī)試圖使用的最大內(nèi)存量。??boolean?removeShutdownHook(Thread?hook)??取消注冊某個先前已注冊的虛擬機(jī)關(guān)閉掛鉤。??void?runFinalization()??運(yùn)行掛起?finalization?的所有對象的終止方法。??static?void?runFinalizersOnExit(boolean?value)??已過時。?此方法本身具有不安全性。它可能對正在使用的對象調(diào)用終結(jié)方法,而其他線程正在操作這些對象,從而導(dǎo)致不正確的行為或死鎖。??long?totalMemory()??返回?Java?虛擬機(jī)中的內(nèi)存總量。??void?traceInstructions(boolean?on)??啟用/禁用指令跟蹤。??void?traceMethodCalls(boolean?on)??啟用/禁用方法調(diào)用跟蹤。?1.3 Process
不管通過哪種方法啟動進(jìn)程后,都會返回一個Process類的實(shí)例代表啟動的進(jìn)程,該實(shí)例可用來控制進(jìn)程并獲得相關(guān)信息。Process 類提供了執(zhí)行從進(jìn)程輸入、執(zhí)行輸出到進(jìn)程、等待進(jìn)程完成、檢查進(jìn)程的退出狀態(tài)以及銷毀(殺掉)進(jìn)程的方法:
void?destroy()??殺掉子進(jìn)程。??一般情況下,該方法并不能殺掉已經(jīng)啟動的進(jìn)程,不用為好。??int?exitValue()??返回子進(jìn)程的出口值。??只有啟動的進(jìn)程執(zhí)行完成、或者由于異常退出后,exitValue()方法才會有正常的返回值,否則拋出異常。??InputStream?getErrorStream()??獲取子進(jìn)程的錯誤流。??如果錯誤輸出被重定向,則不能從該流中讀取錯誤輸出。??InputStream?getInputStream()??獲取子進(jìn)程的輸入流。??可以從該流中讀取進(jìn)程的標(biāo)準(zhǔn)輸出。??OutputStream?getOutputStream()??獲取子進(jìn)程的輸出流。??寫入到該流中的數(shù)據(jù)作為進(jìn)程的標(biāo)準(zhǔn)輸入。??int?waitFor()??導(dǎo)致當(dāng)前線程等待,如有必要,一直要等到由該?Process?對象表示的進(jìn)程已經(jīng)終止。?2.多進(jìn)程編程實(shí)例
一般我們在java中運(yùn)行其它類中的方法時,無論是靜態(tài)調(diào)用,還是動態(tài)調(diào)用,都是在當(dāng)前的進(jìn)程中執(zhí)行的,也就是說,只有一個java虛擬機(jī)實(shí)例在運(yùn)行。而有的時候,我們需要通過java代碼啟動多個java子進(jìn)程。這樣做雖然占用了一些系統(tǒng)資源,但會使程序更加穩(wěn)定,因?yàn)樾聠拥某绦蚴窃诓煌奶摂M機(jī)進(jìn)程中運(yùn)行的,如果有一個進(jìn)程發(fā)生異常,并不影響其它的子進(jìn)程。
在Java中我們可以使用兩種方法來實(shí)現(xiàn)這種要求。最簡單的方法就是通過Runtime中的exec方法執(zhí)行java classname。如果執(zhí)行成功,這個方法返回一個Process對象,如果執(zhí)行失敗,將拋出一個IOException錯誤。下面讓我們來看一個簡單的例子。
//?文件?import?.*;?public?class?Test?{? public?static?void?main(String[]?args)? {?FileOutputStream?fOut?=?new?FileOutputStream("c:\\");?fOut.close();?System.out.println("被調(diào)用成功!");? }?}???//??public?class?Test_Exec?{? public?static?void?main(String[]?args)? {?Runtime?run?=?();?Process?p?=?run.exec("java?test1");? }?}?通過java Test_Exec運(yùn)行程序后,發(fā)現(xiàn)在C盤多了個文件,但在控制臺中并未出現(xiàn)"被調(diào)用成功!"的輸出信息。因此可以斷定,Test已經(jīng)被執(zhí)行成功,但因?yàn)槟撤N原因,Test的輸出信息未在Test_Exec的控制臺中輸出。這個原因也很簡單,因?yàn)槭褂胑xec建立的是Test_Exec的子進(jìn)程,這個子進(jìn)程并沒有自己的控制臺,因此,它并不會輸出任何信息。
如果要輸出子進(jìn)程的輸出信息,可以通過Process中的getInputStream得到子進(jìn)程的輸出流(在子進(jìn)程中輸出,在父進(jìn)程中就是輸入),然后將子進(jìn)程中的輸出流從父進(jìn)程的控制臺輸出。具體的實(shí)現(xiàn)代碼如下如示:
//??import?.*;?public?class?Test_Exec_Out?{? public?static?void?main(String[]?args)? {?Runtime?run?=?();?Process?p?=?run.exec("java?test1");?BufferedInputStream?in?=?new?BufferedInputStream(());?BufferedReader?br?=?new?BufferedReader(new?InputStreamReader(in));?String?s;?while?((s?=?())?!=?null)? System.out.println(s);? }?}?從上面的代碼可以看出,在中通過按行讀取子進(jìn)程的輸出信息,然后在Test_Exec_Out中按每行進(jìn)行輸出。 上面討論的是如何得到子進(jìn)程的輸出信息。那么,除了輸出信息,還有輸入信息。既然子進(jìn)程沒有自己的控制臺,那么輸入信息也得由父進(jìn)程提供。我們可以通過Process的getOutputStream方法來為子進(jìn)程提供輸入信息(即由父進(jìn)程向子進(jìn)程輸入信息,而不是由控制臺輸入信息)。我們可以看看如下的代碼:
//?文件?import?.*;?public?class?Test?{? public?static?void?main(String[]?args)? {?BufferedReader?br?=?new?BufferedReader(new?InputStreamReader(System.in));?System.out.println("由父進(jìn)程輸入的信息:"?+?());? }?}???//??import?.*;?public?class?Test_Exec_In?{? public?static?void?main(String[]?args)? {?Runtime?run?=?();?Process?p?=?run.exec("java?test2");?BufferedWriter?bw?=?new?BufferedWriter(new?OutputStreamWriter(()));?("向子進(jìn)程輸出信息");?();?bw.close();?//?必須得關(guān)閉流,否則無法向子進(jìn)程中輸入信息?//?System.in.read();? }?}?從以上代碼可以看出,Test1得到由Test_Exec_In發(fā)過來的信息,并將其輸出。當(dāng)你不加()和()時,信息將無法到達(dá)子進(jìn)程,也就是說子進(jìn)程進(jìn)入阻塞狀態(tài),但由于父進(jìn)程已經(jīng)退出了,因此,子進(jìn)程也跟著退出了。如果要證明這一點(diǎn),可以在最后加上.read(),然后通過任務(wù)管理器(在windows下)查看java進(jìn)程,你會發(fā)現(xiàn)如果加上()和(),只有一個java進(jìn)程存在,如果去掉它們,就有兩個java進(jìn)程存在。這是因?yàn)?#xff0c;如果將信息傳給Test2,在得到信息后,Test2就退出了。在這里有一點(diǎn)需要說明一下,exec的執(zhí)行是異步的,并不會因?yàn)閳?zhí)行的某個程序阻塞而停止執(zhí)行下面的代碼。因此,可以在運(yùn)行test2后,仍可以執(zhí)行下面的代碼。
exec方法經(jīng)過了多次的重載。上面使用的只是它的一種重載。它還可以將命令和參數(shù)分開,如exec("")可以寫成exec("java", "test2")。exec還可以通過指定的環(huán)境變量運(yùn)行不同配置的java虛擬機(jī)。
除了使用Runtime的exec方法建立子進(jìn)程外,還可以通過ProcessBuilder建立子進(jìn)程。ProcessBuilder的使用方法如下:
//??import?.*;?public?class?Test_Exec_Out?{? public?static?void?main(String[]?args)? {?ProcessBuilder?pb?=?new?ProcessBuilder("java",?"test1");?Process?p?=?();?…?…? }?}?在建立子進(jìn)程上,ProcessBuilder和Runtime類似,不同的ProcessBuilder使用start()方法啟動子進(jìn)程,而Runtime使用exec方法啟動子進(jìn)程。得到Process后,它們的操作就完全一樣的。
ProcessBuilder和Runtime一樣,也可設(shè)置可執(zhí)行文件的環(huán)境信息、工作目錄等。下面的例子描述了如何使用ProcessBuilder設(shè)置這些信息。
ProcessBuilder?pb?=?new?ProcessBuilder("Command",?"arg2",?"arg2",?''');?//?設(shè)置環(huán)境變量?Map<String,?String>?env?=?();?("key1",?"value1");?("key2");?("key2",?("key1")?+?"_test");?("..\abcd");?//?設(shè)置工作目錄?Process?p?=?();?//?建立子進(jìn)程?【編輯推薦】
【責(zé)任編輯:
未麗燕TEL:(010)68476606】
點(diǎn)贊 0
總結(jié)
以上是生活随笔為你收集整理的linux多线程_Java+Linux,深入内核源码讲解多线程之进程的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 做男性不育检需到焦作哪家医院好
- 下一篇: ipad连接电脑_这些应用让iPad生产