进程间通信(总结)
轉自:http://blog.sina.com.cn/s/blog_726c4bd20100ryan.html
信號
進程間通信的方法有: 信號、文件鎖、管道、FIFO、信號量、共享內存、消息隊列最有效,最快的方法是:共享內存
信號量、共享內存、消息隊列是system V IPC
必須要求是親屬進程間才能通信的方法是:管道
?
信號機制:
收到信號后是異步運行的。
信號(signal)機制是Linux系統中最為古老的進程之間的通信機制
信號事件的發生有兩個來源:
硬件來源,比如我們按下了鍵盤或者其它硬件故障;
軟件來源,最常用發送信號的系統函數是kill(), raise(), alarm()和setitimer()等函數,軟件來源還包括一些非法運算等操作。
? ? ? ? ?kill()函數可以對任何進程發送信號,raise()只能對自己發信號,alarm()和setitimer()發送定時信號。
? ? ? ? ?可以用kill ?-l 命令列出所有的信號。
? ? ? ? ?程序在休眠時搜索不到信息。
? ? ? ? ?信息未處理完成時,系統無法運行。
進程對信號的處理
v ? ? ? 進程可以通過三種方式來響應和處理一個信號:
忽略信號、捕捉信號、執行缺省操作
但有兩個信號不能忽略:SIGKILL 和SIGSTOP
信號處理函數的安裝
#include<signal.h>
void( *signal(int sig, void( *func)(int)))(int);
? ? ? ? ?int sigaction(int ?signum,const struct sigacton *act,struct sigaction *oldact);
? ? ? ? ?其中:signal()在可靠的信號系統調用的基礎上實現,是庫函數。它只是兩個參數,不支持信號傳遞信息。第一個參數是要安裝的信號量,第二個參數是對第一個參數指定的信號的處理。
如果func不是函數指針,必須是下列兩個宏:
SIG_IGN:忽略信號。
SIG_DEF:采用系統默認的方式處理信號,執行缺省操作。
返回值:返回先前的信號處理函數指針,如果有錯誤則返回-1。
?
信號的發送
除了內核和超級用戶,并不是每個進程都可以向其他的進程發送信號。
一般的進程只能向具有相同uid和gid的進程發送信號,或向相同進程組中的其他進程發送信號。
常用的發送信號的函數有kill()、raise ()、alarm()、setitimer()、abort() 等。
?
kill()函數:給指定的進程發送某一個信號
#include <sys/types.h>
#include <signal.h>
?int kill(pid_t pid, int sig);
pid>0 ?給PID為pid的進程發送信號
pid=0 ?給同一個進程組的所有進程發送信號
pid<0 且 pid!=-1 ?給進程組ID為-pid的所有進程發送信號
pid=-1 ?給出了自身之外的PID大于1的進程發送信號
第二個參數是要發送的信號值,當第二個參數為0的時候,實際上不會發送任何信號
返回值:成功為0;失敗返回-1。
EINVAL:所發送的信號無效
EPERM:沒有向目標進程發送信號的權限
ESRCH:目標進程不存在或者進程已經終止,處于僵尸。
raise()函數:給進程本身發送一個信號
? ? #include <signal.h>
? ? int raise(int sig); ?(給自己發信號)
相當于kill(getpid(),sig);
返回值:成功為0;失敗返回-1
alarm()函數:是一個簡單定時器,專為SIGALRM信號設計
#include <unistd.h>
unsigned int alarm(unsigned int seconds);
在指定的seconds秒之后,給進程本身發生一個SIGALRM信號
?
setitimer()功能強大更強大的定時器函數,支持3種類型的定時器,但是從本質上,它是和alarm共享同一個進程內的定時器
#include <sys/time.h>
int setitimer(int which, const struct itimerval *value, struct itimerval *ovalue);?
abort():向進程發送SIGABORT信號,默認情況下進程會異常退出
?
文件鎖
當多個進程都需要對同一個文件進行讀寫操作的時候,有時候需要確保進程在一次讀寫操作完成之前,文件不被其他進程修改。文件鎖機制提供了這樣的同步功能。
int fcntl(int fd, int cmd, struct flock *lock);
fd 文件描述符
cmd參數:F_GETLK:獲取文件鎖當前的狀態。
F_SETLK:設置文件鎖。
F_SETLKW:這是F_SETLK的阻塞版本,如果新加的鎖被拒絕,那么進程被阻塞直到可以加鎖。
struct flock{
? ? ? ? …
? ? ? ? ?short l_type; ? ? ? ? ??
? ? ? ? ?short l_whence; ? ? ? ??
? ? ? ? ?off_t l_start; ? ? ? ? ? ? ?
? ? ? ? ?off_t l_len; ? ? ? ? ? ?
? ? ? ? ?…};
l_type類型:
F_RDLCK:讀鎖
F_WRLCK:寫鎖
F_UNLCK:解鎖
為了鎖定整個文件,通常的方法是將l_start指定為0,l_whence指定為SEEK_SEK, l_len指定為0。
?
管道
?
管道是針對于本地計算機的兩個進程之間的通信而設計的通信方法,管道建立后,實際獲得兩個文件描述符:一個用于讀取而另外一個用于寫入。
管道是半雙工的,數據只能向一個方向流動,需要雙方通信時,需要建立起兩個管道。
只能用于父子進程或者兄弟進程之間(具有親緣關心的進程)。
單獨構成一種獨立的文件系統:管道對于管道兩端的進程而言,就是一個文件,但它不是普通的文件,它不屬于某種文件系統,而是單獨構成一種文件系統,并且只存在于內存中。
數據的讀出和寫入:一個進程向管道中寫的內容被管道另一端的進程讀出。寫入的內容每次都添加在管道緩沖區的末尾,并且每次都是從緩沖區的頭部讀出數據。
?
管道的創建
系統調用pipe()用于創建一個管道
int pipe(int filedes[2]);
建立管道: ?
filedes[0]: 為pipe的讀出端
filedes[1]: 為pipe的寫入端
兩個文件描述符數組。
?
FIFO
FIFO不同于管道之處在于它提供一個路徑名與之關聯,以FIFO的文件形式存在于文件系統中
在文件系統中是一個有名字的管道
任何進程都可以打開
進程間無需關聯
注:直接映射到磁盤上,有文件名,故任何進程都可以打開。
FIFO的創建
#include <sys/types.h>
#include <sys/stat.h>
int mkfifo(const char *pathname, mode_t mode);
?
信號量
System IPC中,對于每一個新建的信號量、消息隊列以及共享內存,都有一個在整個系統中唯一的標識符。每個標識符也都有唯一對應的關鍵字,關鍵字的數據類型由系統定義為key_t。
創建/獲取信號量
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
int semget(key_t key, int nsems, int semflg);
Key:鍵值
1. 指定鍵值
2. IPC_PRIVATE
系統指定鍵值
nsems:信號量的數目
信號量集合的數目
Smeflg:信號量標志
1.IPC_CREATE 如果內核中沒有此隊列,則創建它。
2.IPC_EXECL ? ? 當和IPC_CREAT一起使用時,如果隊列已經存在,則返回錯誤。
3.mode_flags: ? ? ? 類似于文件的權限
?
信號量操作
當進程需要申請或者釋放公共資源的時候,可以調用semop()來對信號量進行操作
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
int semop(int semid, struct sembuf *sops, unsigned nsops);
sops:指向sembuf結構的數組指針
unsigned short sem_num:組中包含的信號量數量
short ?sem_op:操作類型的整數
整數:加到信號量的值上
負數:信號量的值減去絕對值,如果小于零,進程阻塞,直到信號量的值至少等于其絕對值
0:導致操作阻塞,直到信號量的值為0才繼續。
?
short ?sem_flg:一個符號位
IPC_NOWAIT: 非阻塞操作
SEM_UNDO:linux會在進程退出的時候自動撤銷該次操作。
?
信號量控制
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
int semctl(int semid, int semnum, int cmd, ...);
第一個參數semid指定信號量集,第二個參數semnum指定了讀信號量集里面的哪一個信號集進行操作(編號),第三個參數cmd指定具體的操作類型(指令)。
當進程創建了新的信號量集的時候,調用了atexit(&sem_delete)函數,使得程序退出時,信號量集被釋放。
?
消息隊列
消息隊列是系統內核地址空間中的一個內部的鏈表。消息可以按照順序發送到隊列中,也可以以幾種不同的方式從隊列中讀取。每一個消息隊列用一個唯一的IPC標識符表示
Msgbuf數據結構
struct msgbuf {
? ? ? ?long mtype; ? ? ??
? ? ? ?char mtext[1]; ? ? ?
};
mtype指消息的類型,它由一個整數來代表,并且它只能是大于0的整數。
mtext是消息數據本身
?
創建消息隊列
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h> ??
int msgget(key_t key, int msgflg);
Key:鍵值
1. 指定鍵值(可有ftok()函數獲得)
2. IPC_PRIVATE ?系統指定鍵值
msgflg :信號量標志
1.IPC_CREATE ?如果內核中沒有此隊列,則創建它。
2.IPC_EXECL ? 當和IPC_CREAT一起使用時,如果隊列已經存在,則返回錯誤。
3.mode_flags:類似于文件的權限
發送和接收消息
msgsnd()系統調用用于向隊列發送一條消息:
int msgsnd(int msqid, struct msgbuf *msgp, size_t msgsz, int msgflg);
msgrcv()系統調用用于從消息隊列讀取一條消息:
ssize_t msgrcv(int msqid, struct msgbuf *msgp, size_t msgsz, long msgtyp, int msgflg);
如果msgtype=0,接收隊列的第一個消息(按時間順序依次收到一個消息),大于0接收隊列中消息類型等于這個值的第一個消息(特定值的消息),小于0接收消息隊列中小于或等于msgtype絕對值的所有消息中的最小一個消息(按編號順序依次收消息)
?
消息隊列的控制
通過msgctl()可以對消息隊列進行控制或者一些屬性的修改:
int msgctl(int msqid, int cmd, struct msqid_ds *buf);
cmd:消息隊列的操作
IPC_STAT:讀取消息隊列的數據結構msqid_ds,并將其存儲在buf指定的地址中。
IPC_SET:設置消息隊列的數據結構msqid_ds中的ipc_perm、msg_qbytes、msg_ctime元素的值。這個值取自buf參數。 ?
IPC_RMID:從系統內核中移走消息隊列。
msgid_ds結構體
struct msqid_ds{
? struct ipc_perm msg_perm; ? ??
? time_t ? ? ? ? ?msg_stime; ? ?
? time_t ? ? ? ? ?msg_rtime; ? ?
? time_t ? ? ? ? ?msg_ctime; ? ?
? unsigned long ? __msg_cbytes;?
? msgqnum_t ? ? ? msg_qnum; ? ??
? msglen_t ? ? ? ?msg_qbytes; ??
? pid_t ? ? ? ? ? msg_lspid; ? ?
? pid_t ? ? ? ? ? msg_lrpid; ? ?
}
共享內存
兩個不同進程A、B共享內存的基本原理是,同一塊物理內存被映射到進程A、B各自的進程地址空間。進程A可以即時看到進程B對共享內存中數據的更新,反之亦然。
?
創建和獲取共享內存
系統調用shmget()用于創建共享內存或者獲取一個已經存在的共享內存的標識符:
int shmget(key_t key, size_t size, int shmflg);
key:鍵值
1. 指定鍵值
2. IPC_PRIVATE ?系統指定鍵值
size:共享內存大小
msgflg :共享內存標志
1.IPC_CREATE ?如果內核中沒有此隊列,則創建它。
2.IPC_EXECL ? 當和IPC_CREAT一起使用時,如果隊列已經存在,則返回錯誤。
3.mode_flags: ? 類似于文件的權限
?
?
系統調用shmat()可以獲取一個共享內存的地址,并將其連接到進程中:
void *shmat(int shmid, const void *shmaddr, int shmflg);
shmid:
由shmget返回的共享內存標志
shmaddr:
映射該共享內存塊的進程內存地址
如果為NULL,Linux將自動選擇合適的地址
shmflg:
SHM_RND
SHM_RDONLY
?
通過shmctl()可以對消息隊列進行控制或者一些屬性的修改:
int shmctl(int shmid, int cmd, struct shmid_ds *buf);
cmd:共享內存的操作
IPC_STAT:讀取一個共享內存的數據結構shmid_ds,并將其存儲在buf指定的地址中。
IPC_SET:設置消息隊列的數據結構shmid_ds中各個元素的值。這個值取自buf參數。
IPC_RMID:把共亨內存標記為可刪除,當最后一個進程脫連此共享內存的時候,系統將刪除該共享內存
總結
- 上一篇: 理解ARC在Objective-C中的应
- 下一篇: 区块链项目包装指南