http://blog.chinaunix.net/uid-25324849-id-3110075.html
部分轉自:http://blog.chinaunix.net/uid-20620288-id-3025213.html
1、首先要明確進程和線程的含義:
進程(Process)是具有一定獨立功能的程序關於某個數據集合上的一次運行活動,是系統進行資源分配和調度的一個獨立單位。與程序相比,程序只是一組指令的有序集合,它本身沒有任何運行的含義,只是一個靜態實體。進程是程序在某個數據集上的執行,是一個動態實體。它因創建而產生,因調度而運行,因等待資源或事件而被處於等待狀態,因完成任務而被撤消,反映了一個程序在一定的數據集上運行的全部動態過程。
每個正在系統上運行的程序都是一個進程。每個進程包含一到多個線程。進程也可能是整個程序或者是部分程序的動態執行。線程是一組指令的集合,或者是程序的特殊段,它可以在程序里獨立執行。也可以把它理解為代碼運行的上下文。所以線程基本上是輕量級的進程,它負責在單個程序里執行多任務。通常由操作系統負責多個線程的調度和執行。
多線程是為了同步完成多項任務,不是為了提高運行效率,而是為了提高資源使用效率來提高系統的效率。線程是在同一時間需要完成多項任務的時候實現的。
使用線程的好處有以下幾點:
a)使用線程可以把占據長時間的程序中的任務放到后台去處理
b)用戶界面可以更加吸引人,這樣比如用戶點擊了一個按鈕去觸發某些事件的處理,可以彈出一個進度條來顯示處理的進度
c)程序的運行速度可能加快
d)在一些等待的任務實現上如用戶輸入、文件讀寫和網絡收發數據等,線程就比較有用了。在這種情況下我們可以釋放一些珍貴的資源如內存占用等等。
2、其次來看下線程和進程的關系
線程是屬於進程的,線程運行在進程空間內,同一進程所產生的線程共享同一內存空間,當進程退出時該進程所產生的線程都會被強制退出並清除。線程可與屬於同一進程的其它線程共享進程所擁有的全部資源,但是其本身基本上不擁有系統資源,只擁有一點在運行中必不可少的信息(如程序計數器、一組寄存器和棧)。
3、然后我們來看下線程和進程間的比較
子進程繼承父進程的屬性: |
子線程繼承主線程的屬性: |
實際用戶ID,實際組ID,有效用戶ID,有效組ID; 附加組ID; 進程組ID; 會話ID; 控制終端; 設置用戶ID標志和設置組ID標志; 當前工作目錄; 根目錄; 文件模式創建屏蔽字(umask); 信號屏蔽和安排; 針對任一打開文件描述符的在執行時關閉(close-on-exec)標志; 環境; 連接的共享存儲段; 存儲映射; 資源限制; |
進程中的所有信息對該進程的所有線程都是共享的; 可執行的程序文本; 程序的全局內存; 堆內存; 棧; 文件描述符; 信號的處理是進程中所有線程共享的(注意:如果信號的默認處理是終止該進程那么即是把信號傳給某個線程也一樣會將進程殺掉);
|
父子進程之間的區別: |
子線程特有的: |
fork的返回值(=0子進程); 進程ID不同; 兩個進程具有不同的父進程ID; 子進程的tms_utime,tms_stime,tms_cutime以及tms_ustime均被設置為0; 不繼承父進程設置的文件鎖; 子進程的未處理鬧鍾被清除; 子進程的未處理信號集設置為空集; |
線程ID; 一組寄存器值; 棧; 調度優先級和策略; 信號屏蔽字; errno變量; 線程私有數據; |
3、設計時考慮的使用技巧
1.盡量避免長駐內存的進程,例如那些很少用到的功能,或周期性很長(10分鍾以上),把它們的功能提取出來,做成一個小的應用程序。需要的時候再把它們拉起來(如通過crontab配置,或直接system)。
2.把目標設計成子功能系統的組合可用提高重用的易用性和維護性。
把目標根據功能划分不同的子系統,子系統間遵循特定的協議(文本或XML),由通訊聯系起來,協作完成目標。
也就是說,我們在做設計的時候可以如下考慮:
1、線程的創建以及線程間的通信和同步都比進程要快。在多核CPU上的任務分割是對線程而言的,不是進程。
2、如果不需要頻繁的創建和銷毀 執行的效率是並不多的,需要頻繁創建的話,線程快。
3、其它的就根據你的實際情況選擇了, 要是沒有數據通信什么的,線程間的通信比進程間方便。最關鍵的一點,多線程可以讓同一個程序的不同部分並發執行。
所以在做安防系統的時候,報警系統和監控系統之間可以用多進程來做,對於報警系統中可以用多線程來實現如果發生意外,可以向用戶發送消息,同時鳴笛,以及如果是火警的話,可以打開閥門等。
進程間通信
unix系統主要進程間通信機制(IPC)
管道
FIFO(命名管道)
消息隊列
共享內存
信號量
套接字
3. 管道
詳細請見:
http://blog.chinaunix.net/space.php?uid=25324849&do=blog&id=207407
管道是最常見的IPC機制,是單工的,如果要兩個進程實現雙向傳輸則需要兩個管道,管道創建的時候既有兩端,一個讀端和一個寫端。兩個進程要協調好,一個進程從讀的方向讀,一個進程從寫的方向寫,並且只能在關系進程間進行,比如父子進程,通過系統調用pipe()函數實現。
#include
int pipe(int fd[2]);
fd[0]:文件描述符,用於讀操作
fd[1]:文件描述符,用於寫操作
返回值:成功返回0,如果創建失敗將返回-1並記錄錯誤碼
4. FIFO
詳細請見:
http://blog.chinaunix.net/space.php?uid=25324849&do=blog&id=207413
FIFO又稱命名管道,通過FIFO的通信可以發生在任何兩個進程之間,且只需要對FIFO有適當的訪問權限,對FIFO的讀寫操作與普通文件類似,命名管道的創建是通過mkfifo()函數創建的。
#include
int mkfifo(const char *filename, mode_t mode)
filename:命名管道的文件名
mode:訪問權限
返回值:若成功則返回0,否則返回-1,錯誤原因存於errno中。
4.1 FIFO服務器實例
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define SERVER_FIFO_NAME "./serv_fifo"
#define CLIENT_FIFO_NAME "./cli_%d_fifo"
#define BUFFER_SIZE 20
struct data_to_pass_st {
pid_t client_pid;
char some_data[BUFFER_SIZE - 1];
};
int main()
{
int server_fifo_fd, client_fifo_fd;
struct data_to_pass_st my_data;
int read_res;
char client_fifo[256];
char *tmp_char_ptr;
mkfifo(SERVER_FIFO_NAME, 0777);
server_fifo_fd = open(SERVER_FIFO_NAME, O_RDONLY);
if (server_fifo_fd == -1) {
fprintf(stderr, "Server fifo failure\n");
exit(EXIT_FAILURE);
}
sleep(10); /* lets clients queue for demo purposes */
do {
read_res = read(server_fifo_fd, &my_data, sizeof(my_data));
if (read_res > 0) {
tmp_char_ptr = my_data.some_data;
while (*tmp_char_ptr) {
*tmp_char_ptr = toupper(*tmp_char_ptr);
tmp_char_ptr++;
}
sprintf(client_fifo, CLIENT_FIFO_NAME, my_data.client_pid);
client_fifo_fd = open(client_fifo, O_WRONLY);
if (client_fifo_fd != -1) {
write(client_fifo_fd, &my_data, sizeof(my_data));
close(client_fifo_fd);
}
}
} while (read_res > 0);
close(server_fifo_fd);
unlink(SERVER_FIFO_NAME);
exit(EXIT_SUCCESS);
}
4.2 FIFO客戶實例
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define SERVER_FIFO_NAME "./serv_fifo"
#define CLIENT_FIFO_NAME "./cli_%d_fifo"
#define BUFFER_SIZE 20
struct data_to_pass_st {
pid_t client_pid;
char some_data[BUFFER_SIZE - 1];
};
int main()
{
int server_fifo_fd, client_fifo_fd;
struct data_to_pass_st my_data;
int times_to_send;
char client_fifo[256];
server_fifo_fd = open(SERVER_FIFO_NAME, O_WRONLY);
if (server_fifo_fd == -1) {
fprintf(stderr, "Sorry, no server\n");
exit(EXIT_FAILURE);
}
my_data.client_pid = getpid();
sprintf(client_fifo, CLIENT_FIFO_NAME, my_data.client_pid);
if (mkfifo(client_fifo, 0777) == -1) {
fprintf(stderr, "Sorry, can't make %s\n", client_fifo);
exit(EXIT_FAILURE);
}
for (times_to_send = 0; times_to_send < 5; times_to_send++) {
sprintf(my_data.some_data, "Hello from %d", my_data.client_pid);
printf("%d sent %s, ", my_data.client_pid, my_data.some_data);
write(server_fifo_fd, &my_data, sizeof(my_data));
client_fifo_fd = open(client_fifo, O_RDONLY);
if (client_fifo_fd != -1) {
if (read(client_fifo_fd, &my_data, sizeof(my_data)) > 0) {
printf("received: %s\n", my_data.some_data);
}
close(client_fifo_fd);
}
}
close(server_fifo_fd);
unlink(client_fifo);
exit(EXIT_SUCCESS);
}
5. 消息隊列
詳細請見:
http://blog.chinaunix.net/space.php?uid=25324849&do=blog&id=207459
消息隊列有如下特點:
(1) 通過消息隊列key值來定義和生成消息隊列
(2) 任何進程只要有訪問權限並且知道key就可以訪問消息隊列
(3) 消息隊列為內存塊方式數據段
(4) 消息隊列的消息長度可為系統參數限制內的任何長度
(5) 消息隊列有消息類型,訪問可以按類型訪問
(6) 在一次讀寫操作前都必須取得消息標識符,即訪問權,訪問后脫離關系
(7) 消息隊列中的某條消息被讀后立即自動的從消息隊列中刪除
(8) 消息隊列具有加鎖處理機制
(9) 在權限允許時,消息隊列的信息可以雙向傳遞
6. 共享內存
詳細請見:
http://blog.chinaunix.net/space.php?uid=25324849&do=blog&id=207467
共享內存是效率最高的IPC機制,他允許任何兩個進程訪問相同的邏輯內存區,它具有一下特點:
(1) 通過共享內存key值定義和生成共享內存
(2) 任何進程只要有訪問權限並且知道key就可以訪問共享內存
(3) 共享內存為內存塊方式數據段
(4) 共享內存的消息長度可為系統參數限制內的任何長度
(5) 共享內存的訪問方式與數組的訪問方式相同
(6) 在取得共享內存標識符將共享內存與進程數據段連接后即可以開始對其進行讀寫操作,在所有操作完成之后再做共享內存與進程數據段的脫離操作,才完成內存訪問的過程
(7) 共享內存中的數據不會因為數據被進程讀取后消失
(8) 共享內存不具備鎖機制,所有共享內存最好與信號量一起使用來保證數據的一致性
(9) 在權限允許時,共享內存的信息傳遞時雙向的
7. 信號量
詳細請見:
http://blog.chinaunix.net/space.php?uid=25324849&do=blog&id=207464
信號量是一種同步機制,主要用途是保護臨界資源(在一個時刻只能被一個進程所擁有),通常與共享內存一起使用。
6.1 semget()函數
#include
int semget(key_t key, int num_sems, int sem_flags)
key:信號量集合的鍵
num_sems:信號量集合里面元素個數
sem_flags:任選參數
返回值:返回信號量集合標識符,出錯返回-1
6.2 semop()函數
#include
int semop(int sem_id, struct sembuf *sem_ops , size_t num_sem_ops)
sem_id: 信號量集合標識符
sem_ops:信號量操作結構的指針
num_sem_ops:信號量操作結構的個數
6.3 semctl)函數
#include
int semctl (int sem_id, int sem_num, int command, …)
sem_id: 信號量集合標識符
sem_num:信號量元素編號
command:控制命令
…:命令參數列表
返回值:根據命令返回相應的值,出錯返回-1
http://timyang.net/linux/linux-process/
上周碰到部署在真實服務器上某個應用CPU占用過高的問題,雖然經過tuning, 問題貌似已經解決,但我對tuning的方式只是基於大膽的假設並最終生效了。我更希望更多的求證一下程序背后CPU及OS kernel當時的運作機制。所以我讀了一些Linux內核設計與實現及其他一些相關資料,對Linux process的機制與切換有了更多一些體會。本文盡可能條理一點,但由於牽涉點較多,同時自己可能覺得某些點有記錄的價值,因此文字可能會零散。
- 進程狀態
Linux進程的狀態比較容易理解,值得注意的是 UNINTERRUPTIBLE 及 ZOMBIE
TASK_RUNNING
TASK_INTERRUPTIBLE
TASK_UNINTERRUPTIBLE 此時進程不接收信號,這就是為什么有時候kill一個繁忙的進程沒有響應。
TASK_ZOMBIE 我們經常 kill -9 pid 之后運行ps會發現被kill的進程仍然存在,狀態為 zombie。zombie的進程實際上已經結束,占用的資源也已經釋放,僅由於kernel的相關進程描述符還未釋放。
TASK_STOPPED
- Kernel space and user space
Kernel space是供內核,設備驅動運行的內存區域。user space是供普通應用程序運行的區域。每一個進程都運行在自己的虛擬內存區域,不能訪問其他進程的內存空間。普通進程不能訪問kernel space, 只能通過系統調用來間接進行。當系統內存比較緊張時,非當前運行進程user space可能會被swap到磁盤。
使用命令 pmap -x 可以查看進程的內存占用信息; lsof -a -p 可以查看一個進程打開的文件信息。ps -Lf 可以查看進程的線程數。
另外procfs也是一個分析進程結構的好地方。procfs是一個虛擬的文件系統,它把系統中正在運行的進程都顯現在/proc/目錄下。
- 進程創建
進程創建通常調用fork實現。創建后子進程和父進程指向同一內存區域,僅當子進程有write發生時候,才會把改動的區域copy到子進程新的地址空間,這就是copy-on-write技術,它極大的提高了創建進程的速度。
- Linux的線程實現
Linux線程是通過進程來實現。Linux kernel為進程創建提供一個clone()系統調用,clone的參數包括如 CLONE_VM, CLONE_FILES, CLONE_SIGHAND 等。通過clone()的參數,新創建的進程,也稱為LWP(Lightweight process)與父進程共享內存空間,文件句柄,信號處理等,從而達到創建線程相同的目的。
Linux 2.6的線程庫叫NPTL(Native POSIX Thread Library)。POSIX thread(pthread)是一個編程規范,通過此規范開發的多線程程序具有良好的跨平台特性。盡管是基於進程的實現,但新版的NPTL創建線程的效率非常高。一些測試顯示,基於NPTL的內核創建10萬個線程只需要2秒,而沒有NPTL支持的內核則需要長達15分鍾。
在Linux 2.6之前,Linux kernel並沒有真正的thread支持,一些thread library都是在clone()基礎上的一些基於user space的封裝,因此通常在信號處理、進程調度(每個進程需要一個額外的調度線程)及多線程之間同步共享資源等方面存在一定問題。為了解決這些問題,當年IBM曾經開發一套NGPT(Next Generation POSIX Threads), 效率比 LinuxThreads有明顯改進,但由於NPTL的推出,NGPT也完成了相關的歷史使命並停止了開發。
NPTL的實現是在kernel增加了futex(fast userspace mutex)支持用於處理線程之間的sleep與wake。futex是一種高效的對共享資源互斥訪問的算法。kernel在里面起仲裁作用,但通常都由進程自行完成。
NPTL是一個1×1的線程模型,即一個線程對於一個操作系統的調度進程,優點是非常簡單。而其他一些操作系統比如Solaris則是MxN的,M對應創建的線程數,N對應操作系統可以運行的實體。(N<m),優點是線程切換快,但實現稍復雜。< p="">
- 信號
進程接收信號有兩種:同步和異步。同步信號比如SEGILL(非法訪問), SIGSEGV(segmentation fault)等。發生此類信號之后,系統會立即轉到內核陷阱處理程序,因此同步信號也稱為陷阱。異步信號如kill, lwp_kill, sigsend等調用產生的都是,異步信號也稱為中斷。
kill 調用的是 SIGTERM, 此信號可以被捕獲和忽略。
kill -9 調用的是 SIGKILL, 殺掉進程,不能被捕獲和忽略。
SIGHUP是在終端被斷開時候調用,如果信號沒有被處理,進程會終止。這就是為什么突然斷網剛通過遠程終端啟動的進程都終止的原因。防止的方法是在啟動的命令前加上 nohup 命令來忽略 SIGHUP信號。如 nohup ./startup.sh &
很多應用程序通常捕獲SIGHUP用來實現一些自定義特性,比如通過控制台傳遞信號讓正在運行的程序重新加載配置文件,避免重啟帶來的停止服務的副作用。可惜的是,在JAVA中沒法直接使用這一功能,SUN JVM沒有官方的signal支持,盡管它已經可以實現,詳情可參看Singals and Java.
另外有個有趣的現象是 zombie 狀態的進程 kill/kill -9 都沒有任何作用,這是由於進程本身已經不存在,所以沒有相應的進程來處理signal, zombie狀態的進程只是kernel中的進程描述符及相關數據結構沒有釋放,但進程實體已經不存在了。
關於僵屍進程,也可參看下酷殼上的這篇Linux 的僵屍(zombie)進程,從程序的角度解釋了相關原理。
(寧波大學科學技術學院理工分院浙江寧波315211)
1.引言
為了提高計算機系統的效率.增強計算機系統內各種硬件
的並行操作能力.操作系統要求程序結構必須適應並發處理的
需要.為此引入了進程的概念。進程是操作系統的核心,所有基
於多道程序設計的操作系統都建立在進程的概念之上。目前的
計算機系統均提供了多任務並行環境.無論是應用程序還是系
統程序.都需要針對每一個任務創建相應的進程。進程是設計和
分析操作系統的有力工具。然而不同的進程之間.即使是具有家
族聯系的父子進程.都具有各自不同的進程映像。由於不同的進
程運行在各自不同的內存空間中.一方對於變量的修改另一方
是無法感知的.因此.進程之間的信息傳遞不可能通過變量或其
它數據結構直接進行,只能通過進程間通信來完成。並發進程之
間的相互通信是實現多進程間協作和同步的常用工具.具有很
強的實用性
2.進程通信分類
進程通信是操作系統內核層極為重要的部分 根據進程通
信時信息量大小的不同,可以將進程通信划分為兩大類型:控制
信息的通信和大批數據信息的通信.前者稱為低級通信,后者稱
為高級通信
低級通信主要用於進程之間的同步、互斥、終止、掛起等等
控制信息的傳遞,主要有以下三種方式:
(1)利用系統調用睡眠SLEEP()和喚醒WAKEUP()實現
進程之間的同步,互斥。SLE EP可以使調用它的進程以指定的優
先級在系統的某個等待隊列上睡眠.而WAKEUP則用來喚醒在
指定隊列上睡眠的進程
(2)利用系統調用WAIT'()和EXIT()實現父子進程之間的
同步 調用WAIT的父進程必須等待子進程運行結束才能繼續
運行,而EXIT則可以用來終止某一個進程,並把相關狀態返回
給它的父進程
(3)利用軟中斷信號實現同一用戶的各進程之間的通信。
高級通信主要用於進程間數據塊的交換和共享 常見的高級通
信有管道(PIPE)、消息隊列(MESSAGE)、共享內存(SHARED
MEM0RY)等。
3.軟中斷通信
軟中斷信號是一種簡單且最基本的進程間通信機制.它最
大的特點是提供了一種簡單的處理異步事件的方法。例如,我們
常見的用戶從鍵盤輸入組合鍵Ctrl+C來中斷一個程序的運行.
或者在兩個進程之間通過某個信號來通知發生了異步事件.或
者向系統或進程報告突發的硬件故障如非法指令、運算溢出等
等 更重要的是用戶進程還可以向自己發送信號以中斷程序的
執行.並自動轉入指定的軟中斷處理函數中去執行用戶自行安
排的處理內容.處理完畢后再返回用戶進程繼續執行.從而為應
用程序提供了由用戶自行處理隨機事件的通信機制。
因此,軟中斷信號實現(signal implementation)是操作系統
用來通知進程有事件發生的一種機制 由於這種信號總是在進
程處於運行狀態時才會去響應的.故稱之為軟中斷信號。
軟中斷信號的使用者是操作系統和用戶源程序.操作系統
事先將系統中可以使用的軟中斷信號進行集中編碼並定義相應
含義后.提交用戶使用。用戶可以通過相應的軟中斷序號或軟中
斷名稱來使用軟中斷
4.管道通信(PIPE)
管道是一種常用的單向進程間通信機制 兩個進程利用管
道進行通信時.發送信息的進程稱為寫進程.接收信息的進程稱
為讀進程。管道通信方式的中間介質就是文件.通常稱這種文件
為管道文件.它就像管道一樣將一個寫進程和一個讀進程連接
在一起,實現兩個進程之間的通信。寫進程通過寫入端(發送端)
往管道文件中寫入信息;讀進程通過讀出端(接收端)從管道文
件中讀取信息。兩個進程協調不斷地進行寫和讀,便會構成雙方
通過管道傳遞信息的流水線。
利用系統調用PIPE()可以創建一個無名管道文件,通常稱
為無名管道或PIPE;利用系統調用MKNOD()可以創建一個有
名管道文件.通常稱為有名管道或FIFO。無名管道是一種非永
久性的管道通信機構.當它訪問的進程全部終止時,它也將隨之
被撤消。無名管道只能用在具有家族聯系的進程之間。有名管道
可以長期存在於系統之中.而且提供給任意關系的進程使用,但
是使用不當容易導致出錯.所以操作系統將命名管道的管理權
交由系統來加以控制
管道文件被創建后,可以通過系統調用WRITE()和READ
()來實現對管道的讀寫操作;通信完畢后,可用CLOSE()將管道
文件關閉。
5.消息緩沖通信(MESSAGE)
多個獨立的進程之間可以通過消息緩沖機制來相互通信.
這種通信的實現是以消息緩沖區為中間介質.通信雙方的發送
和接收操作均以消息為單位。在存儲器中,消息緩沖區被組織成
隊列,通常稱之為消息隊列。消息隊列一旦創建后即可由多進程
共享.發送消息的進程可以在任意時刻發送任意個消息到指定
的消息隊列上,並檢查是否有接收進程在等待它所發送的消息。
若有則喚醒它:而接收消息的進程可以在需要消息的時候到指
定的消息隊列上獲取消息.如果消息還沒有到來.則轉入睡眠狀
態等待。
由於一個消息隊列由多個進程共享.因此掛在隊列上的消
息需要統一規格。以Linux為例.系統定義了一個公用的消息緩
沖區數據結構msgbuf.而消息隊列則是由若干msgbuf構成的鏈
表。利用系統調用MSGGET()可以創建消息隊列,這一步工作也
被稱為消息隊列的初始化。在進行消息緩沖通信時.發送消息進
程使用系統調用MSGSND()將消息掛人消息隊列,接收消息進
程使用系統調用MSGREC()從消息隊列上摘取消息。在需要改
變隊列的使用權限及其它一些特性時,用MSGCTL()來實現。
共享的消息隊列是一個臨界資源,針對同一消息隊列的諸
發送和接收進程必須保證互斥進入,這種進程間的同步和互斥
是由系統提供的系統調用自動實現的,所以用戶在使用時不需
要再考慮它們之間的同步關系.非常方便。
但是消息發送進程在發送消息前必須先請求一個msgbuf,
然后將要發送的消息從私有的地址空間中復制
到msgbuf中才能發送;消息接收進程則相反,必須先從消息隊
列上摘取消息msgbuf.再將msgbuf中的信息復制到自己的程序
空間中。因此,在消息緩沖通信方式中需要進行大量額外的復制
操作,這是消息緩沖通信方式的缺點。
6.共享內存通信(SHARED MEMORY)
針對消息緩沖需要占用CPU進行消息復制的缺點.OS提
供了一種進程間直接進行數據交換的通信方式一共享內存 顧
名思義.這種通信方式允許多個進程在外部通信協議或同步,互
斥機制的支持下使用同一個內存段(作為中間介質)進行通信.
它是一種最有效的數據通信方式,其特點是沒有中間環節.直接
將共享的內存頁面通過附接.映射到相互通信的進程各自的虛
擬地址空間中.從而使多個進程可以直接訪問同一個物理內存
頁面.如同訪問自己的私有空間一樣(但實質上不是私有的而是
共享的)。因此這種進程間通信方式是在同一個計算機系統中的
諸進程間實現通信的最快捷的方法.而它的局限性也在於此.即
共享內存的諸進程必須共處同一個計算機系統.有物理內存可
以共享才行。
與消息緩沖通信類似.在進行共享內存通信之前.必須先通
過系統調用SHMGET()創建一個共享內存段.然后使用系統調
用SHMAT()和SHMDT()來實現共享內存的映射和分離.用系
統調用SHMCTL()來改變共享內存段的存取權限及其它一些特
性。
共享內存通信與消息緩沖通信有很多相似之處:多個進程
都是通過獲取共享內存的標識符來訪問指定的共享內存.都要
進行權限檢查等等。不同的是共享內存一旦附接后就作為進程
地址空間的一部分提供給進程使用 對於該共享內存的讀寫操
作如同對進程私有的緩沖區一樣.操作系統不再關心進程間是
如何使用這個共享內存,更無法進行干預。因此。系統提供的共
享內存系統調用函數是不帶同步工具的. 多個進程對共享內存
的讀寫操作所需要的同步和互斥則必須由各進程通過使用其它
的同步工具來解決。
7.幾種通信方法總結
綜上所述.進程之間的多種通信方法各自有各自的優點和
缺點:
如果用戶傳遞的信息較少.或是需要通過信號來觸發某些
行為.前文提到的軟中斷信號機制不失為一種簡捷有效的進程
間通信方式.但若是進程間要求傳遞的信息量比較大或者進程
間存在交換數據的要求,那就需要考慮別的通信方式了。
無名管道簡單方便.但局限於單向通信的工作方式.並且只
能在創建它的進程及其子孫進程之間實現管道的共享:有名管
道雖然可以提供給任意關系的進程使用.但是由於其長期存在
於系統之中,使用不當容易出錯.所以普通用戶一般不建議使
用。
消息緩沖可以不再局限於父子進程.而允許任意進程通過
共享消息隊列來實現進程間通信.並由系統調用函數來實現消
息發送和接收之間的同步.從而使得用戶在使用消息緩沖進行
通信時不再需要考慮同步問題.使用方便,但是信息的復制需要
額外消耗CPU的時間.不適宜於信息量大或操作頻繁的場合。
共享內存針對消息緩沖的缺點改而利用內存緩沖區直接交
換信息,無須復制,快捷、信息量大是其優點。但是共享內存的通
信方式是通過將共享的內存緩沖區直接附加到進程的虛擬地址
空間中來實現的.因此,這些進程之間的讀寫操作的同步問題操
作系統無法實現。必須由各進程利用其他同步工具解決。另外,
由於內存實體存在於計算機系統中.所以只能由處於同一個計
算機系統中的諸進程共享。不方便網絡通信。
不同的進程通信方式有不同的優點和缺點.因此.對於不同
的應用問題,要根據問題本身的情況來選擇進程間的通信方式。
參考文獻:
1.胡明慶、高魏、鍾梅.操作系統與實驗教程.清華大學出版社.2006年
8月
2.龐麗萍。操作系統原理.牟中科技大學出版社。2ooo卑
3.黃超.1mux高級開發技術.機械工業出版社.2002年