【IO程序】程序間通訊
阿新 • • 發佈:2019-01-24
【1】程序間通訊概述
每一個程序雖然獨立,但也需要讓不同的程序實現資料的傳輸、還有訊號通知 通訊方式: 傳統的程序間通訊: 無名管道、有名管道 --> 資料傳輸 訊號 -》 非同步通知 系統5(System V)通訊方式: 共享記憶體、訊息佇列 --》 資料傳輸 訊號量集 --》 同步和互斥 網路套接字通訊 不同的電腦不同的程序之間實現通訊 不同的程序間如何實現通訊? 在作業系統的所執行的那段空間(核心空間3-4g),開闢一款緩衝區
【2】無名管道
是作業系統在核心空間所開闢的一塊記憶體區域。記憶體區域有一定的大小,大小為64K 無名管道:就是在對當前檔案系統找不到對應的名字,對應的操作函式會給你返回兩個描述符,一個代表管道的讀操作,一個代表管道的寫操作 特點:只能在父子程序或者兄弟程序或者子孫程序之間實現通訊,資料傳輸方向的單向,是一個半雙工的通訊方式。 int pipe(int pipdfd[2]); 功能:在核心空間開間一個記憶體區域用來實現程序間通訊 引數: pipefd[0] 代表管道的讀端,也就是用來從pipefd[0]讀取資料 pipefd[1] 代表管道的寫端,也就是用來從pipefd[1]寫入資料 返回值: 成功 0 失敗 -1 當管道中沒有資料的時候,執行讀操作,read函式會產生阻塞 如果管道有 資料則依照讀取的資料個數進行read操作 往管道中寫資料,如果寫滿管道64k。則write會產生阻塞,只有讀操作讀取資料 管道的剩餘空間大於4k時,write才能繼續寫資料
【3】有名管道
有名管道:可以在檔案系統下找到某個檔案,對檔案對開啟,執行相應的讀寫操作,內部實現與無名管道相同,檔案中僅儲存inode編號,指向記憶體中的區域。 int mkfifo(const char *pathname, mode_t mode); 功能:建立有名管道,用來實現不同的程序間通訊 引數: pathname: 指定管道檔案的名字,檔案型別是p,以後通過開啟管道檔案 往有名管道中寫入資料、或者讀出資料 mode: 檔案許可權(mode & ~umask) 返回值: 成功 0 失敗 -1 有名管道與無名管道的使用方式相同,只需使用open系統呼叫獲取檔案描述符即可。
【4】訊號
實現程序跟程序之間的通知,依據訊號通知,具體執行什麼操作,決定於訊號的預設操作
但是訊號有三種相應(操作)方式:
(1)預設,執行系統設定的操作方式
(2)忽略,程序接收到訊號之後,不做任何操作
(3)自定義操作:需要自己在程序中定義一個訊號處理函式,執行對應訊號操作
kill -l
常用預設訊號的操作方式:
2) SIGINT ctrl+c 結束程序
3) SIGQUIT ctrl+\ 結束程序
9) SIGKILL 結束程序
10) SIGUSR1
12) SIGUSR2 使用者自定義訊號 結束程序
13) SIGPIPE 管道破裂,結束程序
14) SIGALRM 鬧鐘訊號,結束程序
17) SIGCHLD 子程序狀態改變,會向父程序傳送這個訊號
18) SIGCONT 讓暫停的程序恢復執行
20) SIGTSTP 暫停程序
19) SIGSTOP 暫停程序
【注意】9 和 19這兩個訊號的預設操作方式不能被改變
kill(pid_t pid, int sig) --> kill(1000, SIGKILL)
int raise(int sig);
功能: 都是用來發送訊號
pid 程序的pid號
sig 訊號種類中的一種
成功 0
unsigned int seconds alarm(unsigned int seconds)
功能: 用來設定鬧鐘,本身不具有阻塞作用
引數 senonds 設定鬧鐘秒數
返回值:
之前沒有設定鬧鐘,返回值是0
之前有設定過鬧鐘,則返回之前鬧鐘剩餘的時間值
typedef void (*sighandler_t)(int); --> sighandler_t
typedef別名重定義,sighandler_t表示函式指標型別
sighandler_t signal(int signum, sighandler_t handler);
功能:訊號處理函式,首先需要向核心註冊訊號signum,註冊完之後,signal本身不具有阻塞作用,會繼續往後執行
handler 使用者自定操作,也就是使用者需要定義一個函式 void handler(int),這是函式的型別,形參表示的是核心向你這個程序傳送的訊號是什麼?
void handler(int signo)
{
if(signo == SIGKILL)
{
處理操作,實現功能,需要使用者自己定義
}
if(signo == SIGINT)
{
處理操作
}
}
【5】System V 程序間通訊物件
共享記憶體、訊息佇列、訊號量集
也是在核心當中去建立,建立好之後,一直保留系統當中,
如果不用操作核心物件了,需要手動的刪除
a、【刪除】核心物件命令
ipcrm [ -M key | -m id | -Q key | -q id | -S key | -s id ]
舉例:ipcs -m id --》檢視共享記憶體
b、在程式中【檢視】核心物件
int system(const char *command);
引數 :
conmand shell命令
舉例:system(“ls -l”)
核心物件操作流程:
1、建立key值
2、建立核心物件
3、操作核心物件,具體操作,依據核心物件
4、刪除核心物件
key: 讓進城找到核心物件。(共享記憶體、訊息佇列、訊號量集)
IPC_PRIVATE 當前程序建立,只允許當前程序使用 0x00000000
ftok 當一個程序建立好之後,其他的程序可以通過這個key值,找到核心物件
#include <sys/ipc.h>
key_t ftok(const char *pathname, int proj_id);
功能:建立一個key值,讓不同的程序找到核心物件
引數:
pathname 檔名(必須存在,並且可以訪問),取得是檔案的inode節點號
proj_id 整形變數,值得範圍是(1~255)
返回值:
成功 key
失敗 -1
【6】共享記憶體
共享記憶體:是在核心空間中的一塊區域,需要設定,大小也有使用者自己定義,以位元組為單位進行分配,並且是連續的地址空間,在所有通訊當中,效率最高,可以直接方位記憶體空間的地址
#include <sys/shm.h>
int shmget(key_t key, size_t size, int shmflg);
功能: 建立共享記憶體
引數:
key 讓不同的程序找到核心物件
size 共享記憶體的大小是多少? 以位元組為單位
shmflg
IPC_CREAT 表示要建立共享記憶體
IPC_EXCL 如果已經建立,則會判斷是否重複建立
mode_flags 指定9位有效許可權位 0666
舉例: IPC_CREAT | IPC_EXCL | 0666
返回值:
成功 shmid,用來對內和物件(共享記憶體)操作的變數
失敗 -1
void *shmat(int shmid, const void *shmaddr, int shmflg);
功能: 將核心空間的記憶體地址對映到呼叫的使用者空間
引數:
shmid 通過shmget建立或者開啟的共享記憶體
shmaddr NULL 表示系統自動選擇合適的未使用的是給給使用者空間
shmflg
0 表示可讀可寫
SHM_RDONLY 表示只讀
返回值:
成功 對映後的地址,需要進行強制型別轉換
失敗 (void *)-1
int shmdt(const void *shmaddr);
功能:解除對映
引數:
shmaddr 對映後的地址(指標變數)
返回值:
成功 0
失敗 -1
int shmctl(int shmid, int cmd, struct shmid_ds *buf);
功能:共享記憶體控制函式
引數:
shmid 表示共享記憶體
cmd
IPC_RMID IPC_STAT
buf 資訊結構體 NULL struct shmid_ds(定義變數取地址)
返回值:
成功 0
失敗 -1
【注意】
在訪問共享記憶體時,有可能出現多個程序共同訪問的現象,造成資料的混亂或者說是不一致,需要使用訊號量或者互斥鎖來保護共享資源
【7】訊號量集
訊號量集:訊號量的集合,代表一類資源,每一類資源都有數量,需要使用者自己設定
建立訊號量的數目,需要使用者事先指定,並初始化設定每一個訊號量的數量值同樣也需要進行P(申請)V(釋放)操作。
int semget(key_t key, int nsems, int semflg);
功能:建立訊號量集合,裡面可以包含多個訊號量,具體數量自己指定
引數:
key 為了方便其他程序找到訊號量集合
nsems 指定訊號量集合中訊號量的數目是多少個?
1 集合有一個訊號量 0
2 集合有兩個訊號量 0 1
semflg
IPC_CREAT 建立訊號量
IPC_EXCL 防止重複建立
0666 許可權
返回值:
成功 操作識別符號(操作訊號量用的)
失敗 -1
int semctl(int semid, int semnum, int cmd, ...);
功能:訊號量的控制操作函式
引數:
semid 代表你要操作的訊號量集合
sennum 表示你要操作第幾個訊號量
cmd
SETVAL 用來設定每一個訊號量的數量是多少個?
IPC_RMID 用來刪除訊號量集合
union semun
{
int val; /* Value for SETVAL 設定每一個訊號量的數量*/
struct semid_ds *buf; /* Buffer for IPC_STAT, IPC_SET */
unsigned short *array; /* Array for GETALL, SETALL */
struct seminfo *__buf; /* Buffer for IPC_INFO (Linux-specific) */
};
int semop(int semid, struct sembuf *sops, unsigned nsops);
功能:訊號量的pv操作
引數:
semid 操作那個訊號量集合
sops 如何對訊號量進行操作
unsigned short sem_num; 對第幾個訊號量進行操作/* semaphore number */
short sem_op; 執行什麼操作P(正數) V(負數)/* semaphore operation */
short sem_flg; 設定阻塞還是非阻塞方式 0阻塞 IPC_NOWAIT非阻塞/* operation flags */
nsops 表示連續操作幾個訊號量
返回值:
成功 0
失敗 -1
【8】訊息佇列
(1)訊息佇列機制
特點:
1、先進先出
2、按照型別傳送、讀取訊息
使用ipcs -q檢視系統中的訊息
(2)如何建立訊息佇列
int msgget(key_t key, int msgflg);
功能:建立或者開啟訊息佇列
引數:
key:讓不同的程序找到同一個訊息佇列
msgflg:
IPC_CREAT 建立
IPC_EXCL 防止重複建立
0666 許可權
msqid:訊息佇列識別符號
(3)傳送訊息/接收訊息
結構體1:
struct msgbuf
{
long mtype; /* message type, must be > 0 */訊息型別
char mtext[1]; /* message data */訊息正文
};
int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
功能:傳送訊息到訊息佇列
引數:
msgid: 訊息佇列標示符 (用於操作訊息佇列傳送或者接收訊息)
msgp:首先定義一個訊息佇列的結構體,型別如上所示結構體1
msgsz:訊息佇列正文(text)的大小
msgflg:設定為阻塞的方式0 非阻塞的方式 IPC_NOWAIT
ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp,
int msgflg);
功能:從佇列接收訊息
引數:
msgid: 訊息佇列標示符 (用於操作訊息佇列傳送或者接收訊息)
msgp:首先定義一個訊息佇列的結構體,型別如上所示結構體1
msgsz:訊息佇列正文(text)的大小
msgtyp:訊息的型別(必須是大於0的整數)
msgflg:設定為阻塞的方式0 非阻塞的方式 IPC_NOWAIT
(4)刪除訊息佇列
int msgctl(int msqid, int cmd, struct msqid_ds *buf);
功能:操作訊息佇列
引數:
msgid: 訊息佇列標示符 (用於操作訊息佇列傳送或者接收訊息)
cmd:操作訊息佇列的命令
IPC_STAT 獲取訊息佇列屬性資訊的命令,需要定義一個struct msqid_ds結構體
IPC_RMID 刪除訊息佇列的命令