A. Linux中一個進程能訪問另一個進程所在的內存嗎
子進程共享父進程的某些內存,所以一定程度上可以說,子進程能夠訪問父進程的內存
B. Linux 進程間通信方式有哪些
1、無名管道通信
無名管道(pipe):管道是一種半雙工的通信方式,數據只能單向流動,而且只能在具有親緣關系的進程間使用,進程的親緣關系通常是指父子進程關系。
2、高級管道通信
高級管道(popen):將另一個程序當做一個新的進程在當前程序進程中啟動,則它算是當前程序的子進程,這種方式我們稱為高級管道方式。
3、有名管道通信
有名管道(named pipe):有名管道也是半雙工的通信方式,但是它允許無親緣關系進程間的通信。
4、消息隊列通信
消息隊列(message
queue):消息隊列是由消息的鏈表,存放在內核中並由消息隊列標識符標識,消息隊列克服了信號傳遞信息少、管道只能承載無格式位元組流以及緩沖區大小受限等缺點。
5、信號量通信
信號量(semophore):信號量是一個計數器,可以用來控制多個進程對共享資源的訪問,它常作為一種鎖機制,防止某進程正在訪問共享資源時,其他進程訪問該資源。因此,主要作為進程間以及同一進程內不同線程之間的同步手段。
6、信號
信號(sinal):信號是一種比較復雜的通信方式,用於通知接收進程某個事件已經發生。
7、共享內存通信
共享內存(shared
memory):共享內存就是映射一段能被其他進程所訪問的內存,這段共享內存由一個進程創建,但多個進程都可以訪問。共享內存是最快的IPC方式,它是針對其他進程間通信方式運行效率低而專門設計的。它往往與其他通信機制,如信號量,配合使用,來實現進程間的同步和通信。
8、套接字通信
套接字(socket):套接字也是一種進程間通信機制,與其他通信機制不同的是,它可用於不同機器間的進程通信。
C. 如果沒有使用共享內存,操作系統進程之間會不會訪問
不會。操作系統會阻止一個進程訪問另一個進程的內存地址空間。共享內存會要求兩個或更過進程同意刪除這個限制。
D. 進程間的通信方式有
進程間的通信方式有:無名管道、高級管道、有名管道、消息隊列、信號量、信號、共享內存、套接字。
1、無名管道(pipe):管道是一種半雙工的通信方式,數據只能單向流動,而且只能在具有親緣關系的進程間使用。進程的親緣關系通常是指父子進程關系。
2、高級管道(popen):將另一個程序當做一個新的進程在當前程序進程中啟動,則它算是當前程序的子進程,這種方式我們成為高級管道方式。
3、有名管道(named pipe):有名管道也是半雙工的通信方式,但是它允許無親緣關系進程間的通信。
4、消息隊列(message queue):消息隊列是由消息的鏈表,存放在內核中並由消息隊列標識符標識。消息隊列克服了信號傳遞信息少、管道只能承載無格式位元組流以及緩沖區大小受限等缺點。
5、信號量(semophore):信號量是一個計數器,可以用來控制多個進程對共享資源的訪問。它常作為一種鎖機制,防止某進程正在訪問共享資源時,其他進程也訪問該資源。因此,主要作為進程間以及同一進程內不同線程之間的同步手段。
6、信號(sinal):信號是一種比較復雜的通信方式,用於通知接收進程某個事件已經發生。
7、共享內存(shared memory):共享內存就是映射一段能被其他進程所訪問的內存,這段共享內存由一個進程創建,但多個進程都可以訪問。共享內存是最快的IPC方式,它是針對其他進程間通信方式運行效率低而專門設計的。
8、套接字(socket):套解字也是一種進程間通信機制,與其他通信機制不同的是,它可用於不同機器間的進程通信。
E. 進程間通信方式
在操作系統中,一個進程可以理解為是關於計算機資源集合的一次運行活動,其就是一個正在執行的程序的實例。從概念上來說,一個進程擁有它自己的虛擬CPU和虛擬地址空間,任何一個進程對於彼此而言都是相互獨立的,這也引入了一個問題 —— 如何讓進程之間互相通信?
由於進程之間是互相獨立的,沒有任何手段直接通信,因此我們需要藉助操作系統來輔助它們。舉個通俗的例子,假如A與B之間是獨立的,不能彼此聯系,如果它們想要通信的話可以藉助第三方C,比如A將信息交給C,C再將信息轉交給B —— 這就是進程間通信的主要思想 —— 共享資源。
這里要解決的一個重要的問題就是如何避免競爭,即避免多個進程同時訪問臨界區的資源。
共享內存是進程間通信中最簡單的方式之一。共享內存允許兩個或更多進程訪問同一塊內存。當一個進程改變了這塊地址中的內容的時候,其它進程都會察覺到這個更改。
你可能會想到,我直接創建一個文件,然後進程不就都可以訪問了?
是的,但這個方法有幾個缺陷:
Linux下採用共享內存的方式來使進程完成對共享資源的訪問,它將磁碟文件復制到內存,並創建虛擬地址到該內存的映射,就好像該資源本來就在進程空間之中,此後我們就可以像操作本地變數一樣去操作它們了,實際的寫入磁碟將由系統選擇最佳方式完成,例如操作系統可能會批量處理加排序,從而大大提高IO速度。
如同上圖一樣,進程將共享內存映射到自己的虛擬地址空間中,進程訪問共享進程就好像在訪問自己的虛擬內存一樣,速度是非常快的。
共享內存的模型應該是比較好理解的:在物理內存中創建一個共享資源文件,進程將該共享內存綁定到自己的虛擬內存之中。
這里要解決的一個問題是如何將同一塊共享內存綁定到自己的虛擬內存中,要知道在不同進程中使用 malloc 函數是會順序分配空閑內存,而不會分配同一塊內存,那麼要如何去解決這個問題呢?
Linux操作系統已經想辦法幫我們解決了這個問題,在 #include <sys/ipc.h> 和 #include <sys/shm.h> 頭文件下,有如下幾個shm系列函數:
通過上述幾個函數,每個獨立的進程只要有統一的共享內存標識符便可以建立起虛擬地址到物理地址的映射,每個虛擬地址將被翻譯成指向共享區域的物理地址,這樣就實現了對共享內存的訪問。
還有一種相像的實現是採用mmap函數,mmap通常是直接對磁碟的映射——因此不算是共享內存,存儲量非常大,但訪問慢; shmat與此相反,通常將資源保存在內存中創建映射,訪問快,但存儲量較小。
不過要注意一點,操作系統並不保證任何並發問題,例如兩個進程同時更改同一塊內存區域,正如你和你的朋友在線編輯同一個文檔中的同一個標題,這會導致一些不好的結果,所以我們需要藉助信號量或其他方式來完成同步。
信號量是迪傑斯特拉最先提出的一種為解決 同步不同執行線程問題 的一種方法,進程與線程抽象來看大同小異,所以 信號量同樣可以用於同步進程間通信 。
信號量 s 是具有非負整數值的全局變數,由兩種特殊的 原子操作 來實現,這兩種原子操作稱為 P 和 V :
信號量並不用來傳送資源,而是用來保護共享資源,理解這一點是很重要的,信號量 s 的表示的含義為 同時允許最大訪問資源的進程數量 ,它是一個全局變數。來考慮一個上面簡單的例子:兩個進程同時修改而造成錯誤,我們不考慮讀者而僅僅考慮寫者進程,在這個例子中共享資源最多允許一個進程修改資源,因此我們初始化 s 為1。
開始時,A率先寫入資源,此時A調用P(s),將 s 減一,此時 s = 0,A進入共享區工作。
此時,進程B也想進入共享區修改資源,它調用P(s)發現此時s為0,於是掛起進程,加入等待隊列。
A工作完畢,調用V(s),它發現s為0並檢測到等待隊列不為空,於是它隨機喚醒一個等待進程,並將s加1,這里喚醒了B。
B被喚醒,繼續執行P操作,此時s不為0,B成功執行將s置為0並進入工作區。
此時C想要進入工作區......
可以發現,在無論何時只有一個進程能夠訪問共享資源,這就是信號量做的事情,他控制進入共享區的最大進程數量,這取決於初始化s的值。此後,在進入共享區之前調用P操作,出共享區後調用V操作,這就是信號量的思想。
在Linux下並沒有直接的P&V函數,而是需要我們根據這幾個基本的sem函數族進行封裝:
正如其名,管道就如同生活中的一根管道,一端輸送,而另一端接收,雙方不需要知道對方,只需要知道管道就好了。
管道是一種最 基本的進程間通信機制。 管道由pipe函數來創建: 調用pipe函數,會在內核中開辟出一塊緩沖區用來進行進程間通信,這塊緩沖區稱為管道,它有一個讀端和一個寫端。管道被分為匿名管道和有名管道。
匿名管道通過pipe函數創建,這個函數接收一個長度為2的Int數組,並返回1或0表示成功或者失敗:
int pipe(int fd[2])
這個函數打開兩個文件描述符,一個讀端文件,一個寫端,分別存入fd[0]和fd[1]中,然後可以作為參數調用 write 和 read 函數進行寫入或讀取,注意fd[0]只能讀取文件,而fd[1]只能用於寫入文件。
你可能有個疑問,這要怎麼實現通信?其他進程又不知道這個管道,因為進程是獨立的,其他進程看不到某一個進程進行了什麼操作。
是的,『其他』進程確實是不知道,但是它的子進程卻可以!這里涉及到fork派生進程的相關知識,一個進程派生一個子進程,那麼子進程將會復制父進程的內存空間信息,注意這里是復制而不是共享,這意味著父子進程仍然是獨立的,但是在這一時刻,它們所有的信息又是相等的。因此子進程也知道該全局管道,並且也擁有兩個文件描述符與管道掛鉤,所以 匿名管道只能在具有親緣關系的進程間通信。
還要注意,匿名管道內部採用環形隊列實現,只能由寫端到讀端,由於設計技術問題,管道被設計為半雙工的,一方要寫入則必須關閉讀描述符,一方要讀出則必須關閉寫入描述符。因此我們說 管道的消息只能單向傳遞。
注意管道是堵塞的,如何堵塞將依賴於讀寫進程是否關閉文件描述符。如果讀管道,如果讀到空時,假設此時寫埠還沒有被完全關閉,那麼操作系統會假設還有數據要讀,此時讀進程將會被堵塞,直到有新數據或寫埠被關閉;如果管道為空,且寫埠也被關閉,此時操作系統會認為已經沒有東西可讀,會直接退出,並關閉管道。
對於寫一個已經滿了的管道同理而言。
管道內部由內核管理,在半雙工的條件下,保證數據不會出現並發問題。
了解了匿名管道之後,有名管道便很好理解了。在匿名管道的介紹中,我們說其他進程不知道管道和文件描述符的存在,所以匿名管道只適用於具有親緣關系的進程,而命名管道則很好的解決了這個問題 —— 現在管道有一個唯一的名稱了,任何進程都可以訪問這個管道。
注意,操作系統將管道看作一個抽象的文件,但管道並不是普通的文件,管道存在於內核空間中而不放置在磁碟(有名管道文件系統上有一個標識符,沒有數據塊),訪問速度更快,但存儲量較小,管道是臨時的,是隨進程的,當進程銷毀,所有埠自動關閉,此時管道也是不存在的,操作系統將所有IO抽象的看作文件,例如網路也是一種文件,這意味著我們可以採用任何文件方法操作管道,理解這種抽象是很重要的,命名管道就利用了這種抽象。
Linux下,採用mkfifo函數創建,可以傳入要指定的『文件名』,然後其他進程就可以調用open方法打開這個特殊的文件,並進行write和read操作(那肯定是位元組流對吧)。
注意,命名管道適用於任何進程,除了這一點不同外,其餘大多數都與匿名管道相同。
消息隊列亦稱報文隊列,也叫做信箱,是Linux的一種通信機制,這種通信機制傳遞的數據會被拆分為一個一個獨立的數據塊,也叫做消息體,消息體中可以定義類型與數據,克服了無格式承載位元組流的缺陷(現在收到void*後可以知道其原本的格式惹):
同管道類似,它有一個不足就是每個消息的最大長度是有上限的,整個消息隊列也是長度限制的。
內核為每個IPC對象維護了一個數據結構struct ipc_perm,該數據結構中有指向鏈表頭與鏈表尾部的指針,保證每一次插入取出都是O(1)的時間復雜度。
一個進程可以發送信號給另一個進程,一個信號就是一條消息,可以用於通知一個進程組發送了某種類型的事件,該進程組中的進程可以採取處理程序處理事件。
Linux下 unistd.h 頭文件下定義了如圖中的常量,當你在shell命令行鍵入 ctrl + c 時,內核就會前台進程組的每一個進程發送 SIGINT 信號,中止進程。
我們可以看到上述只有30個信號,因此操作系統會為每一個進程維護一個int類型變數sig,利用其中30位代表是否有對應信號事件,每一個進程還有一個int類型變數block,與sig對應,其30位表示是否堵塞對應信號(不調用處理程序)。如果存在多個相同的信號同時到來,多餘信號會被存儲在一個等待隊列中等待。
我們要理解進程組是什麼,每個進程屬於一個進程組,可以有多個進程屬於同一個組。每個進程擁有一個進程ID,稱為 pid ,而每個進程組擁有一個進程組ID,稱為 pgid ,默認情況下,一個進程與其子進程屬於同一進程組。
軟體方面(諸如檢測鍵盤輸入是硬體方面)可以利用kill函數發送信號,kill函數接受兩個參數,進程ID和信號類型,它將該信號類型發送到對應進程,如果該pid為0,那麼會發送到屬於自身進程組的所有進程。
接收方可以採用signal函數給對應事件添加處理程序,一旦事件發生,如果未被堵塞,則調用該處理程序。
Linux下有一套完善的函數用以處理信號機制。
Socket套接字是用與網路中不同主機的通信方式,多用於客戶端與伺服器之間,在Linux下也有一系列C語言函數,諸如socket、connect、bind、listen與accept,我們無需花太多時間研究這些函數,因為我們可能一輩子都不會與他們打交道,對於原理的學習,後續我會對Java中的套接字socket源碼進行剖析。
對於工作而言,我們可能一輩子都用不上這些操作,但作為對於操作系統的學習,認識到進程間是如何通信還是很有必要的。
面試的時候對於這些方法我們不需要掌握到很深的程度,但我們必須要講的來有什麼通信方式,這些方式都有什麼特點,適用於什麼條件,大致是如何操作的,能說出這些,基本足以讓面試官對你十分滿意了。
F. 計算機基礎-內存管理介紹
物理內存,取決於定址空間和實際內存條的大小
因為程序都是直接訪問物理內存,所以一個進程可以修改其他進程的內存數據,甚至可以修改內核地址空間中的數據
一個程序想要訪問自己程序內存的址0x00000000地址,但是在物理內存上分配內存是隨機的,所以訪問0x00000000可能不是自己的內存區域
假設內存是連續分配的(也就是程序在物理內存上是連續的)
1.進程A進來,向os申請了200的內存空間,於是os把0~199分配給A
2.進程B進來,向os申請了5的內存空間,os把200~204分配給它
3.進程C進來,向os申請了100的內存空間,os把205~304分配給它
4.這個時候進程B運行完了,把200~204還給os
但是很長時間以後,只要系統中的出現的進程的大小>5的話,200-204這段空間都不會被分配出去(只要A和C不退出)。過了一段更長的時間,內存中就會出現許許多多200-204這樣不能被利用的碎片
為了解決直接操作物理內存帶來的問題,所以引入了虛擬地址空間,因為這個空間是虛擬的,所以可以無限大。
每個進程都可以有自己的虛擬地址空間
在虛擬地址空間和物理地址空間之間做一一映射,這里映射的是一片連續的物理內存
將內存分為幾個固定的區域,每個區域的大小固定(通常不同),需要載入程序是選擇一個閑置且容量足夠大的分區進行載入
上面共享隊列的固定分區策略可能造成一個小程序佔用一個大分區的情況,從而造成內存里雖然有小分區閑置但無法載入大程序的情況。可以採用多個隊列,給每個分區一個隊列,程序按照大小排在相應的隊列里,如下圖所示,這時一種分開隊列的固定分區
這種方式也有缺點:如果還有空閑分區,但等待的程序不在該分區的等待隊列上,就將造成有空間而不能運行程序的情況
非固定分區的思想在於除了劃分給OS的空間之外,其餘的內存空間是作為一個整體存在的。當一個程序需要佔用內存空間時,就在該片空間裡面分出一個大小剛剛滿足程序所需的空間。再來一個程序時,則在剩下的空間里再這樣分出一塊來。在這種模式下,一個程序可以載入到任何地方,也可以和物理內存一樣大。
因為系統分配給程序的空間分段映射的是一段連續的物理內存,所以空間不夠時可以將程序倒到外置存儲中,再尋找足夠的內存空間載入
要運行兩個相同的程序,只是數據不一樣,那麼我們可不可以進行指令的共享?可以!那麼我們怎麼使在共享的過程中不產生錯誤呢?那就是雙基址:給指令和數據各自配置基址。
其實就是將指令和數據分成不同的段
因為分段技術映射到物理內存上是一段連續的空間,所以無可避免的會造成內存利用率不高的問題
為了增加內存的利用率,需要使用分頁技術
將虛擬內存空間和物理內存空間皆劃分為大小相同的頁面,如4KB、8KB或16KB等,並以頁面作為內存空間的最小分配單位,一個程序的一個頁面可以存放在任意一個物理頁面里。
在分頁系統中,一個程序的虛擬地址由頁面號和頁內偏移值兩部分組成
32位定址系統中,如果頁面大小為4k,則頁面號佔20位,頁內偏移值佔12位
虛擬內存頁到物理內存頁的映射由內存管理單元完成(MMU)
頁表的根本功能是提供從虛擬頁面到物理頁面的映射。因此,頁表的記錄條數與虛擬頁面數相同。此外,內存管理單元依賴於頁表來進行一切與頁面有關的管理活動,這些活動包括判斷某一頁面號是否在內存里,頁面是否受到保護,頁面是否非法空間等等,頁表是一個硬體數據結構
邏輯分段將一個程序按邏輯關系分解為多個段
如果一個程序需要使用比物理內存更大的內存空間怎麼辦?
此時,可以將程序按照功能分成一段一段功能相對完整的單元,一個單元執行完成後再執行下一個單元,這就是重疊(overlay)
相當於一列火車從上海到北京,不需要一整段完整的鐵軌,只需要兩段火車長度的鐵軌,不斷移動鐵軌,就可以讓火車一直往前開
綜合了邏輯分段和分頁的優點,將程序分為多個邏輯段,在每個段裡面又進行分頁,即將分段和分頁組合起來使用。
採用多級頁表,頂級為段表,次級為頁表。由段號在段表裡面獲得所應該使用的頁表,然後在該頁表裡面查找物理頁面號
操作系統核心原理-5.內存管理(上):基本內存管理
操作系統核心原理-5.內存管理(中):分頁內存管理
操作系統核心原理-5.內存管理(下):段式內存管理
基本內存管理的簡單介紹
G. 如何訪問一個進程中的內存
- 在WIN32中,每個應用程序都可「看見」4GB的線性地址空間,其中最開始的4MB和最後的2GB由操作系統保留,剩下不足2GB的空間用於應用程序私有空間。具體分配如下:0xFFFFFFFF-0xC0000000的1GB用於VxD、存儲器管理和文件系統;0xBFFFFFFF-0x80000000的1GB用於共享的WIN32 DLL、存儲器映射文件和共享存儲區;0x7FFFFFFF-0x00400000為每個進程的WIN32專用地址;0x003FFFFF-0x00001000為MS-DOS 和 WIN16應用程序;0x00000FFF-0x00000000為防止使用空指針的4,096位元組。以上都是指邏輯地址,也就是虛擬內存。
---- 虛擬內存通常是由固定大小的塊來實現的,在WIN32中這些塊稱為「頁」,每頁大小為4,096位元組。在Intel CPU結構中,通過在一個控制寄存器中設置一位來啟用分頁。啟用分頁時CPU並不能直接訪問內存,對每個地址要經過一個映射進程,通過一系列稱作「頁表」的查找表把虛擬內存地址映射成實際內存地址。通過使用硬體地址映射和頁表WIN32可使虛擬內存即有好的性能而且還提供保護。利用處理器的頁映射能力,操作系統為每個進程提供獨立的從邏輯地址到物理地址的映射,使每個進程的地址空間對另一個進程完全不可見。WIN32中也提供了一些訪問進程內存空間的函數,但使用時要謹慎,一不小心就有可能破壞被訪問的進程。本文介紹如何讀另一個進程的內存,寫內存與之相似,完善一下你也可以做個 FPE 之類的內存修改工具。好吧,先准備好編程利器Delphi 和參考手 冊 MSDN ,Now begin!
ReadProcessMemory 讀另一個進程的內存,原形如下:
BOOL ReadProcessMemory(
HANDLE hProcess, // 被讀取進程的句柄;
LPCVOID lpBaseAddress, // 讀的起始地址;
LPVOID lpBuffer, // 存放讀取數據緩沖區;
DWORD nSize, // 一次讀取的位元組數;
LPDWORD lpNumberOfBytesRead // 實際讀取的位元組數;
);
hProcess 進程句柄可由OpenProcess 函數得到,原形如下:
HANDLE OpenProcess(
DWORD dwDesiredAccess, // 訪問標志;
BOOL bInheritHandle, // 繼承標志;
DWORD dwProcessId // 進程ID;
);
---- 當然,用完別忘了用 CloseHandle 關閉打開的句柄。讀另一個進程的內存 dwDesiredAccess 須指定為 PROCESS_VM_READ ,寫另一個進程的內存 dwDesiredAccess 須指定為 PROCESS_VM_WRITE ,繼承標志無所謂,進程ID可由 Process32First 和 Process32Next 得到,這兩個函數可以枚舉出所有開啟的進程,這樣進程的信息也就得到了。 Process32First 和 Process32Next是由 TLHelp32 單元提供的,需在 uses 里加上TLHelp32。ToolsHelp32 封裝了一些訪問堆、線程、進程等 的函數,只適用於Win9x,原形如下:
BOOL WINAPI Process32First(
HANDLE hSnapshot //
由 CreateToolhelp32Snapshot 返回
的系統快照句柄;
LPPROCESSENTRY32 lppe // 指向一個 PROCESSENTRY32 結構;
);
BOOL WINAPI Process32Next(
HANDLE hSnapshot // 由 CreateToolhelp32Snapshot 返回
的系統快照句柄;
LPPROCESSENTRY32 lppe // 指向一個 PROCESSENTRY32 結構;
);
hSnapshot 由 CreateToolhelp32Snapshot 返回的系統快照句柄;
CreateToolhelp32Snapshot 原形如下:
HANDLE WINAPI CreateToolhelp32Snapshot(
DWORD dwFlags, // 快照標志;
DWORD th32ProcessID // 進程ID;
);
現在需要的是進程的信息,所以將 dwFlags
指定為 TH32CS_SNAPPROCESS,
th32ProcessID 忽略;PROCESSENTRY32 結構如下:
typedef struct tagPROCESSENTRY32 {
DWORD dwSize; // 結構大小;
DWORD cntUsage; // 此進程的引用計數;
DWORD th32ProcessID; // 進程ID;
DWORD th32DefaultHeapID; // 進程默認堆ID;
DWORD th32MoleID; // 進程模塊ID;
DWORD cntThreads; // 此進程開啟的線程計數;
DWORD th32ParentProcessID;// 父進程ID;
LONG pcPriClassBase; // 線程優先權;
DWORD dwFlags; // 保留;
char szExeFile[MAX_PATH]; // 進程全名;
} PROCESSENTRY32;
---- 至此,所用到的主要函數已介紹完,實現讀內存只要從下到上依次調用上述函數即可,具體參見原代碼:
procere TForm1.Button1Click(Sender: TObject);
var
FSnapshotHandle:THandle;
FProcessEntry32:TProcessEntry32;
Ret : BOOL;
ProcessID : integer;
ProcessHndle : THandle;
lpBuffer:pByte;
nSize: DWORD;
lpNumberOfBytesRead: DWORD;
i:integer;
s:string;
begin
FSnapshotHandle:=CreateToolhelp32Snapshot(
TH32CS_SNAPPROCESS,0);
//創建系統快照
FProcessEntry32.dwSize:=Sizeof(FProcessEntry32);
//先初始化 FProcessEntry32 的大小
Ret:=Process32First(FSnapshotHandle,FProcessEntry32);
while Ret do
begin
s:=ExtractFileName(FProcessEntry32.szExeFile);
if s='KERNEL32.DLL' then
begin
ProcessID:=FProcessEntry32.th32ProcessID;
s:='';
break;
end;
Ret:=Process32Next(FSnapshotHandle,FProcessEntry32);
end;
//循環枚舉出系統開啟的所有進程,找出「Kernel32.dll」
CloseHandle(FSnapshotHandle);
Memo1.Lines.Clear ;
memo1.lines.add('Process ID '+IntToHex(
FProcessEntry32.th32ProcessID,8));
memo1.lines.Add('File name '+FProcessEntry32.szExeFile);
////輸出進程的一些信息
nSize:=4;
lpBuffer:=AllocMem(nSize);
ProcessHndle:=OpenProcess(PROCESS_VM_READ,false,ProcessID);
memo1.Lines.Add ('Process Handle '+intTohex(ProcessHndle,8));
for i:=$00800001 to $0080005f do
begin
ReadProcessMemory(
ProcessHndle,
Pointer(i),
lpBuffer,
nSize,
lpNumberOfBytesRead
);
s:=s+intTohex(lpBuffer^,2)+' ';
//讀取內容
if (i mod 16) =0 then
begin
Memo1.Lines.Add(s);
s:='';
end;
//格式化輸出
end;
FreeMem(lpBuffer,nSize);
CloseHandle(ProcessHndle);
//關閉句柄,釋放內存
end;
H. 線程、進程在跨線程(進程)訪問內存,許可權有什麼不同
說法一:進程是具有一定獨立功能的程序關於某個數據集合上的一次運行活動,進程是系統進行資源分配和調度的一個獨立單位.
線程是進程的一個實體,是CPU調度和分派的基本單位,它是比進程更小的能獨立運行的基本單位.線程自己基本上不擁有系統資源,只擁有一點在運行中必不可少的資源(如程序計數器,一組寄存器和棧),但是它可與同屬一個進程的其他的線程共享進程所擁有的全部資源.
一個線程可以創建和撤銷另一個線程;同一個進程中的多個線程之間可以並發執行
說法二:進程和線程都是由操作系統所體會的程序運行的基本單元,系統利用該基本單元實現系統對應用的並發性。進程和線程的區別在於:
簡而言之,一個程序至少有一個進程,一個進程至少有一個線程.
線程的劃分尺度小於進程,使得多線程程序的並發性高。
另外,進程在執行過程中擁有獨立的內存單元,而多個線程共享內存,從而極大地提高了程序的運行效率。
線程在執行過程中與進程還是有區別的。每個獨立的線程有一個程序運行的入口、順序執行序列和程序的出口。但是線程不能夠獨立執行,必須依存在應用程序中,由應用程序提供多個線程執行控制。
從邏輯角度來看,多線程的意義在於一個應用程序中,有多個執行部分可以同時執行。但操作系統並沒有將多個線程看做多個獨立的應用,來實現進程的調度和管理以及資源分配。這就是進程和線程的重要區別。
說法三:多線程共存於應用程序中是現代操作系統中的基本特徵和重要標志。用過UNIX操作系統的讀者知道進程,在UNIX操作系統中,每個應用程序的執行都在操作系統內核中登記一個進程標志,操作系統根據分配的標志對應用程序的執行進行調度和系統資源分配,但進程和線程有什麼區別呢?
進程和線程都是由操作系統所體會的程序運行的基本單元,系統利用該基本單元實現系統對應用的並發性。進程和線程的區別在於:
線程的劃分尺度小於進程,使得多線程程序的並發性搞。
另外,進程在執行過程中擁有獨立的內存單元,而多個線程共享內存,從而極大地提高了程序的運行效率。
線程在執行過程中與進程還是有區別的。每個獨立的線程有一個程序運行的入口、順序執行序列和程序的出口。但是線程不能夠獨立執行,必須依存在應用程序中,由應用程序提供多個線程執行控制。
從邏輯角度來看,多線程的意義在於一個應用程序中,有多個執行部分可以同時執行。但操作系統並沒有將多個線程看做多個獨立的應用,來實現進程的調度和管理以及資源分配。這就是進程和線程的重要區別。
進程(Process)是最初定義在Unix等多用戶、多任務操作系統環境下用於表示應用程序在內存環境中基本執行單元的概念。以Unix操作系統為例,進程是Unix操作系統環境中的基本成分、是系統資源分配的基本單位。Unix操作系統中完成的幾乎所有用戶管理和資源分配等工作都是通過操作系統對應用程序進程的控制來實現的。
C、C++、Java等語言編寫的源程序經相應的編譯器編譯成可執行文件後,提交給計算機處理器運行。這時,處在可執行狀態中的應用程序稱為進程。從用戶角度來看,進程是應用程序的一個執行過程。從操作系統核心角度來看,進程代表的是操作系統分配的內存、CPU時間片等資源的基本單位,是為正在運行的程序提供的運行環境。進程與應用程序的區別在於應用程序作為一個靜態文件存儲在計算機系統的硬碟等存儲空間中,而進程則是處於動態條件下由操作系統維護的系統資源管理實體。多任務環境下應用程序進程的主要特點包括:
●進程在執行過程中有內存單元的初始入口點,並且進程存活過程中始終擁有獨立的內存地址空間;
●進程的生存期狀態包括創建、就緒、運行、阻塞和死亡等類型;
●從應用程序進程在執行過程中向CPU發出的運行指令形式不同,可以將進程的狀態分為用戶態和核心態。處於用戶態下的進程執行的是應用程序指令、處於核心態下的應用程序進程執行的是操作系統指令。
在Unix操作系統啟動過程中,系統自動創建swapper、init等系統進程,用於管理內存資源以及對用戶進程進行調度等。在Unix環境下無論是由操作系統創建的進程還要由應用程序執行創建的進程,均擁有唯一的進程標識(PID)。
說法四:應用程序在執行過程中存在一個內存空間的初始入口點地址、一個程序執行過程中的代碼執行序列以及用於標識進程結束的內存出口點地址,在進程執行過程中的每一時間點均有唯一的處理器指令與內存單元地址相對應。
Java語言中定義的線程(Thread)同樣包括一個內存入口點地址、一個出口點地址以及能夠順序執行的代碼序列。但是進程與線程的重要區別在於線程不能夠單獨執行,它必須運行在處於活動狀態的應用程序進程中,因此可以定義線程是程序內部的具有並發性的順序代碼流。
Unix操作系統和Microsoft Windows操作系統支持多用戶、多進程的並發執行,而Java語言支持應用程序進程內部的多個執行線程的並發執行。多線程的意義在於一個應用程序的多個邏輯單元可以並發地執行。但是多線程並不意味著多個用戶進程在執行,操作系統也不把每個線程作為獨立的進程來分配獨立的系統資源。進程可以創建其子進程,子進程與父進程擁有不同的可執行代碼和數據內存空間。而在用於代表應用程序的進程中多個線程共享數據內存空間,但保持每個線程擁有獨立的執行堆棧和程序執行上下文(Context)。
基於上述區別,線程也可以稱為輕型進程 (Light Weight Process,LWP)。不同線程間允許任務協作和數據交換,使得在計算機系統資源消耗等方面非常廉價。
線程需要操作系統的支持,不是所有類型的計算機都支持多線程應用程序。Java程序設計語言將線程支持與語言運行環境結合在一起,提供了多任務並發執行的能力。這就好比一個人在處理家務的過程中,將衣服放到洗衣機中自動洗滌後將大米放在電飯鍋里,然後開始做菜。等菜做好了,飯熟了同時衣服也洗好了。
需要注意的是:在應用程序中使用多線程不會增加 CPU 的數據處理能力。只有在多CPU 的計算機或者在網路計算體系結構下,將Java程序劃分為多個並發執行線程後,同時啟動多個線程運行,使不同的線程運行在基於不同處理器的Java虛擬機中,才能提高應用程序的執行效率。