進程之間的通信方式
①管道(Pipe)及有名管道(Named Pipe): 管道可用於具有"血緣"關系進程間(也就是父子進程或者兄弟進程)的通信。有名管道除具有管道所具有的功能外,還允許無"血緣"關系進程間的通信。
②信號(Signal): 信號是在軟件層次上對中斷機制的一種模擬,它是比較復雜的通信方式,用於通知進程有某事件發生。應該學過ARM或單片機中斷吧,其實一個進程收到一個信號與處理器收到一個中斷請求效果上可以說是一樣的(注意是效果噢!)。
③信號量(Semaphore): 主要作為進程之間及同一進程的不同線程之間的同步和互斥手段。
④共享內存(Shared Memory): 可以說這是最有效的進程間通信方式。它使得多個進程可以訪問同一塊內存空間,不同進程可以及時看到對方進程中對共享內存中數據的更新。這種通信方式需要依靠某種同步機制,如互此鎖和信號量等。
⑤消息隊列(Messge Queue): 消息隊列是消息的鏈表,包括 Posix 消息隊列和 System V 消息隊列。它克服了前兩種通信方式中信息量有限的缺點,具有寫權限的進程可以按照一定的規則向消息隊列中添加消息;對消息隊列具有讀權限的進程則可以從消息隊列中讀取消息。
⑥套接字(Socket): 這個絕對是一種更為一般的進程間通信機制,它可用於網絡中不同機器之間的進程間通。
1.管道
無名管道
創建管道可以調用 pipe() 來實現,如下表
詳細鏈接:Linux進程間通信(二)---管道通信之無名管道及其基礎實驗
有名管道
mkfifo()函數
FIFO相關的出錯信息進行歸納
access()函數
access()函數的功能是確定文件或文件夾的訪問權限,即檢查某個文件的存取方式,比如說是只讀方式、只寫方式等,如果指定的存取方式有效,則函數返回0,否則函數返回-1。
如果讀進程不讀走管道緩沖區中的數據,那么寫操作將一直阻塞。
Linux管道讀寫阻塞
詳細鏈接:Linux進程間通信(三)---管道通信之有名管道及其基礎實驗
2.信號
信號是在軟件層次上對中斷機制的一種模擬,它也是異步的,一個進程不必通過任何操作來等待信號的到達。
信號事件發生的來源有兩種:
① 硬件來源:例如按下了鍵盤上的按鈕 或者出現其他硬件故障;
② 軟件來源:最常用發送信號的系統函數有kill()、raise()、alarm()、setitimer()和sigqueue(),軟件來源還包括一些非法運算等操作。
1.除了內核和超級用戶,並不是每個進程都可以向其他的進程發送信號
2.一般的進程只能向具有相同 uid 和 gid 的進程發送信號,或向相同進程組中的其他進程發送信號
關於setitimer()的使用可以看鏈接:Linux 下setitimer函數的使用
Linux中的常見信號
查看信號命令:kill -l
xzj@xzj-VirtualBox:~/development_test/process/signal$ kill -l
1) SIGHUP 2) SIGINT 3) SIGQUIT 4) SIGILL 5) SIGTRAP
6) SIGABRT 7) SIGBUS 8) SIGFPE 9) SIGKILL 10) SIGUSR1
11) SIGSEGV 12) SIGUSR2 13) SIGPIPE 14) SIGALRM 15) SIGTERM
16) SIGSTKFLT 17) SIGCHLD 18) SIGCONT 19) SIGSTOP 20) SIGTSTP
21) SIGTTIN 22) SIGTTOU 23) SIGURG 24) SIGXCPU 25) SIGXFSZ
26) SIGVTALRM 27) SIGPROF 28) SIGWINCH 29) SIGIO 30) SIGPWR
31) SIGSYS 34) SIGRTMIN 35) SIGRTMIN+1 36) SIGRTMIN+2 37) SIGRTMIN+3
38) SIGRTMIN+4 39) SIGRTMIN+5 40) SIGRTMIN+6 41) SIGRTMIN+7 42) SIGRTMIN+8
43) SIGRTMIN+9 44) SIGRTMIN+10 45) SIGRTMIN+11 46) SIGRTMIN+12 47) SIGRTMIN+13
48) SIGRTMIN+14 49) SIGRTMIN+15 50) SIGRTMAX-14 51) SIGRTMAX-13 52) SIGRTMAX-12
53) SIGRTMAX-11 54) SIGRTMAX-10 55) SIGRTMAX-9 56) SIGRTMAX-8 57) SIGRTMAX-7
58) SIGRTMAX-6 59) SIGRTMAX-5 60) SIGRTMAX-4 61) SIGRTMAX-3 62) SIGRTMAX-2
63) SIGRTMAX-1 64) SIGRTMAX
列表中,編號為1 ~ 31的信號為傳統UNIX支持的信號,是不可靠信號(非實時的),編號為32 ~ 63的信號是后來擴充的,稱做可靠信號(實時信號)。不可靠信號和可靠信號的區別在於前者不支持排隊,可能會造成信號丟失,而后者不會。
編號小於SIGRTMIN的信號的說明
信號 | 說明 |
---|---|
①SIGHUP |
本信號在用戶終端連接(正常或非正常)結束時發出, 通常是在終端的控制進程結束時, 通知同一session內的各個作業, 這時它們與控制終端不再關聯。 |
SIGINT |
程序終止(interrupt)信號, 在用戶鍵入INTR字符(通常是Ctrl-C)時發出,用於通知前台進程組終止進程。 |
SIGQUIT |
和SIGINT類似, 但由QUIT字符(通常是Ctrl-)來控制. 進程在因收到SIGQUIT退出時會產生core文件, 在這個意義上類似於一個程序錯誤信號。 |
SIGILL |
執行了非法指令. 通常是因為可執行文件本身出現錯誤, 或者試圖執行數據段. 堆棧溢出時也有可能產生這個信號。 |
SIGTRAP |
由斷點指令或其它trap指令產生. 由debugger使用。 |
SIGABRT |
調用abort函數生成的信號。 |
SIGBUS |
非法地址, 包括內存地址對齊(alignment)出錯。比如訪問一個四個字長的整數, 但其地址不是4的倍數。它與SIGSEGV的區別在於后者是由於對合法存儲地址的非法訪問觸發的(如訪問不屬於自己存儲空間或只讀存儲空間)。 |
SIGFPE |
在發生致命的算術運算錯誤時發出. 不僅包括浮點運算錯誤, 還包括溢出及除數為0等其它所有的算術的錯誤。 |
SIGKILL |
用來立即結束程序的運行. 本信號不能被阻塞、處理和忽略。如果管理員發現某個進程終止不了,可嘗試發送這個信號。 |
SIGUSR1 |
留給用戶使用 |
SIGSEGV |
試圖訪問未分配給自己的內存, 或試圖往沒有寫權限的內存地址寫數據. |
SIGUSR2 |
留給用戶使用 |
SIGPIPE |
管道破裂。這個信號通常在進程間通信產生,比如采用FIFO(管道)通信的兩個進程,讀管道沒打開或者意外終止就往管道寫,寫進程會收到SIGPIPE信號。此外用Socket通信的兩個進程,寫進程在寫Socket的時候,讀進程已經終止。 |
SIGALRM |
時鍾定時信號, 計算的是實際的時間或時鍾時間. alarm函數使用該信號. |
SIGTERM |
程序結束(terminate)信號, 與SIGKILL不同的是該信號可以被阻塞和處理。通常用來要求程序自己正常退出,shell命令kill缺省產生這個信號。如果進程終止不了,我們才會嘗試SIGKILL。 |
SIGCHLD |
子進程結束時, 父進程會收到這個信號。如果父進程沒有處理這個信號,也沒有等待(wait)子進程,子進程雖然終止,但是還會在內核進程表中占有表項,這時的子進程稱為僵屍進程。 這種情況我們應該避免(父進程或者忽略SIGCHILD信號,或者捕捉它,或者wait它派生的子進程,或者父進程先終止,這時子進程的終止自動由init進程來接管)。 |
SIGCONT |
讓一個停止(stopped)的進程繼續執行. 本信號不能被阻塞. 可以用一個handler來讓程序在由stopped狀態變為繼續執行時完成特定的工作. 例如, 重新顯示提示符 |
SIGSTOP |
停止(stopped)進程的執行. 注意它和terminate以及interrupt的區別:該進程還未結束, 只是暫停執行. 本信號不能被阻塞, 處理或忽略. |
SIGTSTP |
停止進程的運行, 但該信號可以被處理和忽略. 用戶鍵入SUSP字符時(通常是Ctrl-Z)發出這個信號 |
SIGTTIN |
當后台作業要從用戶終端讀數據時, 該作業中的所有進程會收到SIGTTIN信號. 缺省時這些進程會停止執行. |
SIGTTOU |
類似於SIGTTIN, 但在寫終端(或修改終端模式)時收到. |
SIGURG |
有"緊急"數據或out-of-band數據到達socket時產生. |
SIGXCPU |
超過CPU時間資源限制. 這個限制可以由getrlimit/setrlimit來讀取/改變。 |
SIGXFSZ |
當進程企圖擴大文件以至於超過文件大小資源限制。 |
SIGVTALRM |
虛擬時鍾信號. 類似於SIGALRM, 但是計算的是該進程占用的CPU時間. |
SIGPROF |
類似於SIGALRM/SIGVTALRM, 但包括該進程用的CPU時間以及系統調用的時間. |
SIGWINCH |
窗口大小改變時發出. |
SIGIO |
文件描述符准備就緒, 可以開始進行輸入/輸出操作. |
SIGPWR |
Power failure |
SIGSYS |
非法的系統調用 |
kill()函數語法要點
說明 | 詳細 |
---|---|
所需頭文件 | #include <signal.h> #include <sys/types.h> |
函數原型 | int kill(pid_t pid, int sig) |
函數傳入值 | pid 正數:要發送信號的進程號 0:信號被發送到所有和當前進程在同一個進程組的進程 1:信號發給所有的進程表中的進程(除了進程號大的進程外) <-1:信號發送給進程組號為-pid的每一個進程 sig:信號 |
函數返回值 | 成功:0 出錯:-1 |
raise()函數的語法要點
說明 | 詳細 |
---|---|
所需頭文件 | #include <signal.h> #include <sys/types.h> |
函數原型 | int raise(int sig) |
函數傳入值 | sig:信號 |
函數返回值 | 成功:0 出錯:-1 |
/*信號發送函數,kill_raise.cpp*/
#include <sys/types.h>
#include <unistd.h>
#include <sys/wait.h>
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
int main(int argc, char const *argv[])
{
/* code */
pid_t pid;
int res;
pid = fork();//創建一個子進程
if (pid < 0) {
printf("fork has error\n");
exit(1);
}else if (0 == pid) {
//子進程
printf("I am son_process,my PID is %d\n", getpid());
raise(SIGSTOP);//子進程給自己發送停止信號
printf("I am son_process,my pid is %d,I am killed by my father_process(pid= %d)\n", getpid(),getppid());
exit(0);
}else {
//父進程
printf("I am father_process,my pid is %d\n",getpid());
sleep(3);//讓子進程執行一段時間,不然父進程直接就把子進程殺死了
res = waitpid(pid,NULL,WNOHANG);//如果子進程沒有退出,則父進程不阻塞,返回為0;
if (0 == res) {
if (0 == (kill(pid,SIGKILL))) {
printf("I am father_process(pid=%d),I killed my son_process(pid=%d)\n",getpid(),pid);
}
}
waitpid(pid,NULL,0);//等待子進程退出,否則父進程就一直等待。
exit(0);
}
return 0;
}
信號處理有兩種:
1.使用 signal() 函數;
使用signal()函數處理時,只需指出要處理的信號和處理函數即可。它主要用於前32種非實時信號的處理,不支持信號傳遞信息,但是由於使用簡單、易於理解,因此也受到很多程序員的歡迎。Linux還支持一個更健壯更新的信號處理函數sigaction(),推薦使用該函數。
signal()函數語法要點
說明 | 詳細 |
---|---|
所需頭文件 | #include <signal.h> |
函數原型 | typedef void (*sighandler_t)(int); sighandler_t signal(int signum, sighandler_t handler); |
函數傳入值 | signum:指定信號代碼 handler SIG_IGN:忽略該信號 SIG_DFL:采用系統默認方式處理信號 自定義的信號處理函數指針 |
函數返回值 | 成功:以前的信號處理配置 出錯:-1 |
這里需要對該函數原型進行說明。這個函數原型有點復雜:首先該函數原型整體指向一個無返回值並且帶一個整型參數的函數指針,也就是信號的原始配置函數;接着該原型又帶有兩個參數,其中第2個參數可以是用戶自定義的信號處理函數的函數指針。
/*signal.cpp*/
#include <signal.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/time.h>
void my_func(int sig_no)
{
if (sig_no == SIGALRM) {
printf("I want sleep 3 seconds\n");
}else if (sig_no == SIGQUIT) {
printf("I have get a SIGQUIT\n");
}else if (sig_no == SIGINT) {
printf("I have get a SIGINT\n");
signal(SIGINT,SIG_DFL);
}
}
int main(int argc, char const *argv[])
{
struct itimerval tv, otv;
//how long to run the first time
tv.it_value.tv_sec = 1;
tv.it_value.tv_usec = 0;
//after the first time, how long to run next time
tv.it_interval.tv_sec = 3;
tv.it_interval.tv_usec = 0;
signal(SIGALRM,my_func);
signal(SIGQUIT,my_func);/*ctrl+\*/
signal(SIGINT,my_func);//ctrl+c
setitimer(ITIMER_REAL, &tv, NULL);
while(1){
sleep(1);
}
return 0;
}
2.使用信號集函數組;
sigaction()函數的語法要點
說明 | 詳細 |
---|---|
所需頭文件 | #include <signal.h> |
函數原型 | int sigaction(int signum, const struct sigaction * act, struct sigaction * oldact) |
函數傳入值 | signum:信號代碼,可以為除SIGKILL及SIGSTOP外的任何一個特定有效的信號 act:指向結構sigaction的一個實例的指針,指定對特定信號的處理 oldact:保存原來對相應信號的處理 |
函數返回值 | 成功:0 出錯:-1 |
這里要說明的是sigaction()函數中第2和第3個參數用到的sigaction結構
struct sigaction
{
void (*sa_handler)(int signo);
sigset_t sa_mask;
int sa_flags;
void (*sa_restore)(void);
}
①sa_handler是一個函數指針,指定信號處理函數,這里除可以是用戶自定義的處理函數外,還可以為SIG_DFL(采用默認的處理方式)或SIG_IGN(忽略信號)。它的處理函數只有一個參數,即信號值。
②sa_mask是一個信號集,它可以指定在信號處理程序執行過程中哪些信號應當被屏蔽,在調用信號捕獲函數前,該信號集要加入到信號的信號屏蔽字中。
③ sa_flags中包含了許多標志位,是對信號進行處理的各個選擇項,見下表
常見信號的含義及其默認操作
信 號 | 含 義 |
---|---|
SA_NODEFER / SA_NOMASK | 當捕捉到此信號時,在執行其信號捕捉函數時,系統不會自動屏蔽此信號 |
SA_NOCLDSTOP | 進程忽略子進程產生的任何SIGSTOP、SIGTSTP、SIGTTIN和SIGTTOU信號 |
SA_RESTART | 令重啟的系統調用起作用 |
SA_ONESHOT / SA_RESETHAND | 自定義信號只執行一次,在執行完畢后恢復信號的系統默認動作 |
使用信號集函數組處理信號時涉及一系列的函數,這些函數按照調用的先后次序可分為以下幾大功能模塊:創建信號集、注冊信號處理函數及檢測信號。
其中,創建信號集主要用於處理用戶感興趣的一些信號,其函數包括以下幾個。
● sigemptyset():將信號集初始化為空。
● sigfillset():將信號集初始化為包含所有已定義的信號集。
● sigaddset():將指定信號加入到信號集中。
● sigdelset():將指定信號從信號集中刪除。
● sigismember():查詢指定信號是否在信號集中。
注冊信號處理函數主要用於決定進程如何處理信號。這里要注意的是,信號集里的信號並不是真正可以處理的信號,只有當信號的狀態處於非阻塞狀態時才會真正起作用。因此,首先使用sigprocmask()函數檢測並更改信號屏蔽字(信號屏蔽字是用來指定當前被阻塞的一組信號,它們不會被進程接收),然后使用sigaction()函數來定義進程接收到特定信號后的行為。
檢測信號是信號處理的后續步驟,因為被阻塞的信號不會傳遞給進程,所以這些信號就處於“未處理”狀態(也就是進程不清楚它的存在)。sigpending()函數允許進程檢測“未處理”信號,並進一步決定對它們做何處理。
首先介紹創建信號集的函數格式,下表列舉了這一組函數的語法要點。
創建信號集函數語法要點
說明 | 詳細 |
---|---|
所需頭文件 | #include <signal.h> |
函數原型 | int sigemptyset(sigset_t * set) int sigfillset(sigset_t * set) int sigaddset(sigset_t * set, int signum) int sigdelset(sigset_t * set, int signum) int sigismember(sigset_t * set, int signum) |
函數傳入值 | set:信號集 signum:指定信號代碼 |
函數返回值 | 成功:0(sigismember成功返回1,失敗返回0) 出錯:-1 |
sigprocmask()函數語法要點
說明 | 詳細 |
---|---|
所需頭文件 | #include <signal.h> |
函數原型 | int sigprocmask(int how, const sigset_t * set, sigset_t * oset) |
函數傳入值 | how:決定函數的操作方式 SIG_BLOCK:增加一個信號集到當前進程的阻塞集中 SIG_UNBLOCK:從當前的阻塞集中刪除一個信號集 SIG_SETMASK:將當前的信號集設置為信號阻塞集 set:指定信號集 oset:信號屏蔽字 |
函數返回值 | 成功:0 出錯:-1 |
此處,若set是一個非空指針,則參數how表示函數的操作方式;若how為空,則表示忽略此操作。
sigpending()函數語法要點
說明 | 詳細 |
---|---|
所需頭文件 | #include <signal.h> |
函數原型 | int sigpending(sigset_t * set) |
函數傳入值 | set:要檢測的信號集 |
函數返回值 | 成功:0 出錯:-1 |
在處理信號時,一般遵循如圖所示的操作流程
/*sigaction.cpp*/
#include <signal.h>
#include <sys/types.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
void my_func(int sig_no)
{
if (sig_no == SIGQUIT) {
printf("I get a signal of SIGQUIT\n");
}else if (sig_no == SIGINT) {
printf("I get a signal of SIGINT\n");
}
}
int main(int argc, char const *argv[])
{
/* code */
struct sigaction sigac;//聲明sigaction結構體變量
/*sigaction結構體變量的初始化*/
sigac.sa_handler = my_func;//指定信號處理函數
sigemptyset(&sigac.sa_mask);//將屏蔽信號集置為空,一定要使用引用的方式
sigac.sa_flags = 0;
/*通過信號跳轉到處理函數中*/
sigaction(SIGQUIT,&sigac,NULL);
sigaction(SIGINT,&sigac,NULL);
pause();
return 0;
}
/*sigaction_set.cpp*/
#include <sys/types.h>
#include <signal.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
void my_func(int sig_no)
{
if (sig_no == SIGQUIT) {
printf("I get a signal of SIGQUIT,if you want to exit,please try (ctrl +c)\n");
}else if (sig_no == SIGINT) {
printf("I get a signal of SIGINT\n");
}
}
int main(int argc, char const *argv[])
{
/* code */
struct sigaction action1,action2;//聲明結構體變量
sigset_t signal_set;//聲明信號集
if (sigemptyset(&signal_set) < 0) {//初始化信號集為空
perror("sigemptyset:");
exit(1);
}
/*添加信號到信號集*/
if (sigaddset(&signal_set,SIGQUIT) < 0) {
perror("sigaddset:");
exit(1);
}
if (sigaddset(&signal_set,SIGINT) < 0) {
perror("sigaddset:");
exit(1);
}
/*檢測信號是否存在信號集中,並且通過信號跳轉到處理函數中*/
if (sigismember(&signal_set,SIGQUIT)) {
action1.sa_handler = SIG_DFL;//采用系統默認的處理方式
sigemptyset(&action1.sa_mask);//初始化acton1的信號集
action1.sa_flags = 0;
sigaction(SIGQUIT,&action1,NULL);
}
if (sigismember(&signal_set,SIGINT)) {
action2.sa_handler = my_func;//采用系統默認的處理方式
sigemptyset(&action2.sa_mask);//初始化acton1的信號集
action2.sa_flags = 0;
sigaction(SIGQUIT,&action2,NULL);
}
/*設置信號屏蔽,增加一個信號集到當前進程的阻塞集中*/
if (0 == sigprocmask(SIG_BLOCK,&signal_set,NULL)) {
printf("%s\n","信號集被阻塞,請按任何鍵取消阻塞" );
getchar();
}else {
perror("sigprocmask:");
exit(1);
}
if (0 == sigprocmask(SIG_UNBLOCK,&signal_set,NULL)) {
printf("%s\n", "信號集從阻塞集中移除");
}else {
perror("sigprocmask:");
exit(1);
}
while(1);
return 0;
}
自定義信號量:
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
#define SIG_MY_MSG1 SIGRTMIN+10
#define SIG_MY_MSG2 SIGRTMIN+11
void sig_usr(int);
int main(void)
{
pid_t ppid = getpid();
if(signal(SIG_MY_MSG1, sig_usr) == SIG_ERR)
printf("can not catch SIG_MY_MSG1\n");
if(signal(SIG_MY_MSG2, sig_usr) == SIG_ERR)
printf("can not catch SIG_MY_MSG2\n");
//pid_t ppid = getpid();
kill(ppid,SIG_MY_MSG1);
kill(ppid,SIG_MY_MSG2);
pause();
}
void sig_usr(int sig_no)
{
if(sig_no == SIG_MY_MSG1)
printf("received SIG_MY_MSG1\n");
else if(sig_no == SIG_MY_MSG2)
printf("received SIG_MY_MSG2\n");
else
printf("received signal %d\n", sig_no);
}
sigqueue函數
功能:新的發送信號系統調用,主要是針對實時信號提出的支持信號帶有參數,與函數sigaction()配合使用。
原型:int sigqueue(pid_t pid, int sig, const union sigval value);
參數:
sigqueue的第一個參數是指定接收信號的進程id,第二個參數確定即將發送的信號,第三個參數是一個聯合數據結構union sigval,指定了信號傳遞的參數,即通常所說的4字節值。
返回值:成功返回0,失敗返回-1
typedef union sigval
{
int sival_int;
void *sival_ptr;
}sigval_t;
sigqueue()比kill()傳遞了更多的附加信息,但sigqueue()只能向一個進程發送信號,而不能發送信號給一個進程組。
信號發送函數sigqueue和信號安裝函數sigaction
信號量編程(Semaphore)
函數說明
在Linux系統中,使用信號量通常分為以下幾個步驟:
(1)創建信號量或獲得在系統中已存在的信號量,此時需要調用semget()函數。不同進程通過使用同一個信號量鍵值來獲得同一個信號量。
(2)初始化信號量,此時使用semctl()函數的SETVAL操作。當使用二維信號量時,通常將信號量初始化為1。
(3)進行信號量的PV操作,此時調用semop()函數。這一步是實現進程間的同步和互斥的核心工作部分。
(4)如果不需要信號量,則從系統中刪除它,此時使用semctl ()函數的IPC_RMID操作。需要注意的是,在程序中不應該出現對已經被刪除的信號量的操作。
semget()函數語法要點:創建一個新的信號量或獲取一個已經存在的信號量的鍵值
說明 | 詳細 |
---|---|
所需頭文件 | #include <sys/types.h> #include <sys/ipc.h> #include <sys/sem.h> |
函數原型 | int semget(key_t key, int nsems, int semflg) |
函數傳入值 | key:信號量的鍵值,多個進程可以通過它訪問同一個信號量,其中有個特殊值IPC_PRIVATE,用於創建當前進程的私有信號量,該值通常為0,鍵值不是IPC_PRIVATE,我們可以指定鍵值,例如1234;也可以一個ftok()函數來取得一個唯一的鍵值 nsems:需要創建的信號量數目,通常取值為1 semflg:同open()函數的權限位,也可以用八進制表示法,其中使用IPC_CREAT標志創建新的信號量,即使該信號量已經存在(具有同一個鍵值的信號量已在系統中存在),也不會出錯。如果同時使用IPC_EXCL標志可以創建一個新的唯一的信號量,此時如果該信號量已經存在,該函數會返回出錯 |
函數返回值 | 成功:信號量標識符,在信號量的其他函數中都會使用該值 出錯:-1 |
semget的使用
/*semget.cpp*/
#include <sys/sem.h>
#include <stdlib.h>
#include <stdio.h>
#define SEM_KEY 1314
int main(int argc, const char** argv)
{
int sem_id;
sem_id = semget(SEM_KEY,1,IPC_CREAT|0666);//創建一個權限為666的信號量
if (sem_id == -1)
{
/* code */
printf("create the semaphore error");
exit(1);
}
printf("sem_id = %d\n",sem_id);
return 0;
}
我們可以用 ipcs –s 來查看是否創建成功。
xzj@xzj-VirtualBox:~/development_test/process/Semaphore$ ./semget_main
sem_id = 0
xzj@xzj-VirtualBox:~/development_test/process/Semaphore$ ipcs -s
--------- 信號量數組 -----------
鍵 semid 擁有者 權限 nsems
0x00000522 0 xzj 666 1
用 ipcrm -s semid 號來刪除指定的信號量。
xzj@xzj-VirtualBox:~/development_test/process/Semaphore$ ipcrm -s 0
xzj@xzj-VirtualBox:~/development_test/process/Semaphore$ ipcs -s
--------- 信號量數組 -----------
鍵 semid 擁有者 權限 nsems
semctl()函數語法要點:控制信號量的函數,在這個函數中我們可以刪除信號量或初始化信號量
說明 | 詳細 |
---|---|
所需頭文件 | #include <sys/types.h> #include <sys/ipc.h> #include <sys/sem.h> |
函數原型 | int semctl(int semid, int semnum, int cmd, union semun arg) |
函數傳入值 | semid:semget()函數返回的信號量標識符 semnum:信號量編號,當使用信號量集時才會被用到。通常取值為0,就是使用單個信號量(也是第一個信號量) cmd:指定對信號量的各種操作,當使用單個信號量(而不是信號量集)時,常用的操作有以下幾種。 ● IPC_STAT:獲得該信號量(或者信號量集)的semid_ds結構,並存放在由第4個參數arg結構變量的buf域指向的semid_ds結構中。semid_ds是在系統中描述信號量的數據結構 ● IPC_SETVAL:將信號量值設置為arg的val值 ● IPC_GETVAL:返回信號量的當前值 ● IPC_RMID:從系統中刪除信號量(或者信號量集) arg:是union semnn結構,可能在某些系統中不給出該結構的定義,此時必須由程序員自己定義 union semun { int val; struct semid_ds *buf; unsigned short *array; } |
函數返回值 | 成功:根據cmd值的不同而返回不同的值 IPC_STAT、IPC_SETVAL、IPC_RMID:返回0 IPC_GETVAL:返回信號量的當前值 出錯:-1 |
semop()函數語法要點:用戶改變信號量的值。也就是使用資源還是釋放資源使用權
代碼:讓子進程先行執行,再執行父進程
/**
* sem_fork.cpp
* 信號量同步互斥父子進程
*/
#include <stdlib.h>
#include <stdio.h>
#include <sys/sem.h>
#include <sys/ipc.h>
#include <sys/types.h>
#include <unistd.h>
#define DELAY_TIME 6 //為了表示父進程開始被阻塞,子進程先執行完后,父進程才執行,
/*define union by myself */
union semun
{
int val;
struct semid_ds *buf;
unsigned short *array;
};
/* 函數聲明 */
int init_sem(int sem_id,int init_vlaue);//初始化信號量
int delete_sem(int sem_id);//刪除信號量
int sem_p(int sem_id);//信號量P操作
int sem_v(int sem_id);//信號量V操作
int main(int argc, const char** argv)
{
pid_t ret_fork;
int sem_id;
/*創建信號量 */
sem_id = semget(1314,1,IPC_CREAT|0666);//創建一個權限為666的信號量,信號量鍵值人為定義:1314,當然可以ftok()函數定義信號量的鍵值
if (sem_id == -1)
{
/* code */
printf("create the semaphore error");
exit(1);
}
init_sem(sem_id,0);//把信號量的值初始化為0,阻塞父進程,讓子進程先執行,以達到同步的效果
/* 創建子進程 */
ret_fork = fork();
if (-1 == ret_fork) {
perror("創建進程:");
exit(1);
}else if (0 == ret_fork) {
/*子進程 */
sleep(DELAY_TIME);
printf("I am son_process,my pid is :%d\n",getpid());
sem_v(sem_id);//執行V操作,喚醒父進程
}else {
/* 父進程 */
sem_p(sem_id);//執行信號量(鍵值為1314)的P操作
printf("I am father_process,my pid is :%d\n",getpid());
sem_v(sem_id);
delete_sem(sem_id);
}
return 0;
}
/*信號量初始化(賦值)函數 */
int init_sem(int sem_id,int init_vlaue)
{
union semun sem_union;
sem_union.val = init_vlaue;//對信號量值賦值為init_value
if (-1 == semctl(sem_id,0,SETVAL,sem_union)) {
perror("初始化信號量:");
return -1;
}
return 0;
}
/*從系統中刪除信號量 */
int delete_sem(int sem_id)
{
union semun sem_union;//聲明結構體
if (-1 == semctl(sem_id,0,IPC_RMID,sem_union)) {
perror("刪除信號量:");
return -1;
}
return 0;
}
/*信號量-P操作 */
int sem_p(int sem_id)
{
struct sembuf sops;
sops.sem_num = 0;//單個信號量取值為0;
sops.sem_op = -1;//信號量的操作標志,-1表示進行P操作
sops.sem_flg = SEM_UNDO;//當進程在沒有釋放所占信號量而意外退出時,系統會自動釋放該進程未釋放的信號量
if (-1 == semop(sem_id,&sops,1)) {
perror("信號量P操作:");
return -1;
}
return 0;
}
/*信號量-V操作 */
int sem_v(int sem_id)
{
struct sembuf sops;
sops.sem_num = 0;//單個信號量取值為0;
sops.sem_op = 1;//信號量的操作標志,-1表示進行P操作
sops.sem_flg = SEM_UNDO;//當進程在沒有釋放所占信號量而意外退出時,系統會自動釋放該進程未釋放的信號量
if (-1 == semop(sem_id,&sops,1)) {
perror("信號量V操作:");
return -1;
}
return 0;
}
結果:
xzj@xzj-VirtualBox:~/development_test/process/Semaphore$ ./sem_fork_main
I am son_process,my pid is :11359
I am father_process,my pid is :11358
共享內存
共享內存是一種為高效的進程間通信方式,因為進程可以直接讀寫內存,不需要任何數據的復制。為了在多個進程間交換信息,內核專門留出了一塊內存區,這段內存區可以由需要訪問的進程將其映射到自己的私有地址空間。因此,進程就可以直接讀寫這一內存區而不需要進行數據的復制,從而大大提高了效率。當然,由於多個進程共享一段內存,因此也需要依靠某種同步機制,如互斥鎖和信號量
原理如圖所示:
共享內存使用步驟:
① 創建共享內存。也就是從內存中獲得一段共享內存區域,這里用到的函數是shmget();
② 映射共享內存。也就是把這段創建的共享內存映射到具體的進程空間中,這里使用的函數是shmat()。到這一步就可以使用這段共享內存了,也就是可以使用不帶緩沖的I/O讀寫命令對其進行操作。
③ 撤銷映射。使用完共享內存就需要撤銷,用到的函數是shmdt()。
shmget()函數語法
shmat()函數語法
shmdt()函數語法
shmctl()函數 -- ctl:control
與信號量的semctl()函數一樣,用來控制共享內存,它的原型如下:
int shmctl(int shm_id, int command, struct shmid_ds *buf);
第一個參數,shm_id是shmget()函數返回的共享內存標識符。
第二個參數,command是要采取的操作,它可以取下面的三個值 :
IPC_STAT:把shmid_ds結構中的數據設置為共享內存的當前關聯值,即用共享內存的當前關聯值覆蓋shmid_ds的值。
IPC_SET:如果進程有足夠的權限,就把共享內存的當前關聯值設置為shmid_ds結構中給出的值
IPC_RMID:刪除共享內存段
第三個參數,buf是一個結構指針,它指向共享內存模式和訪問權限的結構。
shmid_ds結構 至少包括以下成員:
struct shmid_ds
{
uid_t shm_perm.uid;
uid_t shm_perm.gid;
mode_t shm_perm.mode;
};
共享內存與信號量結合實現進程的通信
1.頭文件semaphore.h
#ifndef SEMAPHORE_H
#define SEMAPHORE_H
/*define union by myself */
union semun
{
int val;
struct semid_ds *buf;
unsigned short *array;
};
/* 函數聲明 */
int init_sem(int sem_id,int init_vlaue);//初始化信號量
int delete_sem(int sem_id);//刪除信號量
int sem_p(int sem_id);//信號量P操作
int sem_v(int sem_id);//信號量V操作
#endif // !SEMAPHORE_H
2.實現頭文件的源文件sharemem.cpp
#include "semaphore.h"
#include <stdlib.h>
#include <stdio.h>
#include <sys/ipc.h>
#include <sys/types.h>
#include <sys/sem.h>
#include <unistd.h>
/*信號量初始化(賦值)函數 */
int init_sem(int sem_id,int init_vlaue)
{
union semun sem_union;
sem_union.val = init_vlaue;//對信號量值賦值為init_value
if (-1 == semctl(sem_id,0,SETVAL,sem_union)) {
perror("初始化信號量:");
return -1;
}
return 0;
}
/*從系統中刪除信號量 */
int delete_sem(int sem_id)
{
union semun sem_union;//聲明結構體
if (-1 == semctl(sem_id,0,IPC_RMID,sem_union)) {
perror("刪除信號量:");
return -1;
}
return 0;
}
/*信號量-P操作 */
int sem_p(int sem_id)
{
struct sembuf sops;
sops.sem_num = 0;//單個信號量取值為0;
sops.sem_op = -1;//信號量的操作標志,-1表示進行P操作
sops.sem_flg = SEM_UNDO;//當進程在沒有釋放所占信號量而意外退出時,系統會自動釋放該進程未釋放的信號量
if (-1 == semop(sem_id,&sops,1)) {
perror("信號量P操作:");
return -1;
}
return 0;
}
/*信號量-V操作 */
int sem_v(int sem_id)
{
struct sembuf sops;
sops.sem_num = 0;//單個信號量取值為0;
sops.sem_op = 1;//信號量的操作標志,-1表示進行P操作
sops.sem_flg = SEM_UNDO;//當進程在沒有釋放所占信號量而意外退出時,系統會自動釋放該進程未釋放的信號量
if (-1 == semop(sem_id,&sops,1)) {
perror("信號量V操作:");
return -1;
}
return 0;
}
3.寫進程-sheme_write.cpp
/* sheme_write.cpp */
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <unistd.h>
#include <sys/sem.h>
#include <sys/shm.h>
#include <string.h>
#include "semaphore.h"
#define SEM_KEY 1314 //信號量鍵值
#define MAX_SIZE 2048 //緩沖池大小
#define SHM_KEY 1234 //共享內存鍵值
#define SHM_SIZE 4096 //共享內存大小
int main(int argc, char const *argv[])
{
int sem_id;
int shm_id;
char *shm_addr = NULL;//共享內存被映射的段地址
char buffer[MAX_SIZE] = {0};//緩沖池初始化
sem_id = semget(SEM_KEY,1,IPC_CREAT|0666);//創建一個權限為666的信號量
if (sem_id == -1)
{
/* code */
printf("create the semaphore error\n");
exit(1);
}else {
printf("create semaphore successful!,sem_id is %d\n",sem_id);
}
init_sem(sem_id,1);//初始化信號量的值為0;讓寫先執行。
/* 創建共享內存 */
if (-1 == (shm_id =shmget(ftok(".",'m'),SHM_SIZE, IPC_CREAT | 0777))) {
//if (-1 == (shm_id =shmget(IPC_PRIVATE,SHM_SIZE,0666))) {
perror("共享內存創建shmget:");
exit(1);
}else {
printf("create sharemem successful!,shm_id is %d\n",shm_id);
}
system("ipcs -m");//查看共享內存狀態
/* 共享內存映射 */
if ((shm_addr = (char*)shmat(shm_id,0,0)) ==(char*)-1) {
perror("write:shmat:");
exit(1);
}else {
printf("write:共享內存映射成功!\n");
}
system("ipcs -m");//查看共享內存狀態
printf("請輸入字符串文本:\n");
scanf("%[^\n]",buffer);//字符串輸入
printf("你要將此數據%s寫入到共享內存中!\n",buffer);
sem_p(sem_id);
strncpy(shm_addr,buffer,strlen(buffer));//把緩沖池的數據寫入到共享內存
printf("寫數據成功\n");
/* 解除共享內存的映射 */
if (-1 == shmdt(shm_addr)) {
perror("write:shmdt:");
exit(1);
}else {
printf("write:共享內存解除映射成功!\n");
}
system("ipcs -m");//查看共享內存狀態
sem_v(sem_id);//進行信號量的v操作
//獲取信號量的值
union semun sem_union;
int sem_value;
sem_value = semctl(sem_id,0,GETVAL,sem_union);
printf("sem_id = %d,the sem_value is %d\n",sem_id,sem_value);
exit(0);
//return 0;
}
4.讀進程-sheme_read.cpp
/* sheme_read.cpp */
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <unistd.h>
#include <sys/sem.h>
#include <sys/shm.h>
#include <string.h>
#include "semaphore.h"
#define SEM_KEY 1314 //信號量鍵值
#define MAX_SIZE 2048 //緩沖池大小
#define SHM_KEY 1234 //共享內存鍵值
#define SHM_SIZE 4096 //共享內存大小
int main(int argc, char const *argv[])
{
int sem_id;
int shm_id;
char *shm_addr = NULL;//共享內存被映射的段地址
char buffer[MAX_SIZE] = {0};//緩沖池初始化
// if (argc >1){
// shm_id = atoi(argv[1]);
// }else
// {
// printf("無法獲取shm_id\n");
// exit(1);
// }
sem_id = semget(SEM_KEY,1,IPC_CREAT|0666);//創建一個權限為666的信號量
if (sem_id == -1)
{
/* code */
printf("get the semaphore error\n");
exit(1);
}
printf("成功獲取信號量id:%d\n",sem_id);
/* 進入臨界資源區 */
sem_p(sem_id);//對信號量進行p操作
/* 根據SHM_KEY獲取共享內存 */
if (-1 == (shm_id =shmget(ftok(".",'m'),SHM_SIZE,IPC_CREAT | 0777))) {
perror("共享內存獲取shmget:");
exit(1);
}else {
printf("get sharemem successful!,shm_id is %d\n",shm_id);
}
system("ipcs -m");//查看共享內存狀態
/* 共享內存映射 */
printf("成功獲取共享內存id:%d\n",shm_id);
if ((shm_addr = (char*)shmat(shm_id,0,0)) ==(char*)-1) {
perror("red:shmat:");
exit(1);
}else {
printf("red:共享內存映射成功!\n");
}
/* 從共享內存里讀 */
strcpy(buffer,shm_addr);
printf("從共享內存里讀取的數據為:%s\n",buffer);
/* 解除共享內存的映射 */
if (-1 == shmdt(shm_addr)) {
perror("write:shmdt:");
exit(1);
}else {
printf("write:共享內存解除映射成功!\n");
}
//刪除共享內存
if (-1 == shmctl(shm_id,IPC_RMID,NULL)) {
perror("delete share_emeory:");
exit(1);
}else {
printf("delete shared_memory successful!\n");
}
system("ipcs -m");//查看共享內存狀態
sem_v(sem_id);//進行信號量的v操作
delete_sem(sem_id);
return 0;
}
結果:
寫進程
xzj@xzj-VirtualBox:~/development_test/process/sharemem$ ./sheme_write_main
create semaphore successful!,sem_id is 327680
create sharemem successful!,shm_id is 3407884
------------ 共享內存段 --------------
鍵 shmid 擁有者 權限 字節 連接數 狀態
0x00000000 360448 xzj 600 16777216 2
0x00000000 458753 xzj 600 524288 2 目標
0x00000000 622594 xzj 600 524288 2 目標
0x00000000 294915 xzj 600 524288 2 目標
0x00000000 1245188 xzj 700 16472 2 目標
0x00000000 753669 xzj 600 67108864 2 目標
0x00000000 851974 xzj 600 524288 2 目標
0x00000000 1048583 xzj 600 524288 2 目標
0x00000000 1146888 xzj 600 524288 2 目標
0x00000000 1343497 xzj 600 524288 2 目標
0x00000000 1441802 xzj 600 524288 2 目標
0x00000000 1540107 xzj 600 524288 2 目標
0x6d010f3c 3407884 xzj 777 4096 0
0x00000000 2261005 xzj 600 524288 2 目標
0x00000000 2621454 xzj 700 161700 2 目標
0x00000000 2293775 xzj 600 67108864 2 目標
write:共享內存映射成功!
------------ 共享內存段 --------------
鍵 shmid 擁有者 權限 字節 連接數 狀態
0x00000000 360448 xzj 600 16777216 2
0x00000000 458753 xzj 600 524288 2 目標
0x00000000 622594 xzj 600 524288 2 目標
0x00000000 294915 xzj 600 524288 2 目標
0x00000000 1245188 xzj 700 16472 2 目標
0x00000000 753669 xzj 600 67108864 2 目標
0x00000000 851974 xzj 600 524288 2 目標
0x00000000 1048583 xzj 600 524288 2 目標
0x00000000 1146888 xzj 600 524288 2 目標
0x00000000 1343497 xzj 600 524288 2 目標
0x00000000 1441802 xzj 600 524288 2 目標
0x00000000 1540107 xzj 600 524288 2 目標
0x6d010f3c 3407884 xzj 777 4096 1
0x00000000 2261005 xzj 600 524288 2 目標
0x00000000 2621454 xzj 700 161700 2 目標
0x00000000 2293775 xzj 600 67108864 2 目標
請輸入字符串文本:
I am xzj,I am working in iauto!
你要將此數據I am xzj,I am working in iauto!寫入到共享內存中!
寫數據成功
write:共享內存解除映射成功!
------------ 共享內存段 --------------
鍵 shmid 擁有者 權限 字節 連接數 狀態
0x00000000 360448 xzj 600 16777216 2
0x00000000 458753 xzj 600 524288 2 目標
0x00000000 622594 xzj 600 524288 2 目標
0x00000000 294915 xzj 600 524288 2 目標
0x00000000 1245188 xzj 700 16472 2 目標
0x00000000 753669 xzj 600 67108864 2 目標
0x00000000 851974 xzj 600 524288 2 目標
0x00000000 1048583 xzj 600 524288 2 目標
0x00000000 1146888 xzj 600 524288 2 目標
0x00000000 1343497 xzj 600 524288 2 目標
0x00000000 1441802 xzj 600 524288 2 目標
0x00000000 1540107 xzj 600 524288 2 目標
0x6d010f3c 3407884 xzj 777 4096 0
0x00000000 2261005 xzj 600 524288 2 目標
0x00000000 2621454 xzj 700 161700 2 目標
0x00000000 2293775 xzj 600 67108864 2 目標
sem_id = 327680,the sem_value is 1
xzj@xzj-VirtualBox:~/development_test/process/sharemem$
讀進程
xzj@xzj-VirtualBox:~/development_test/process/sharemem$ ./sheme_read_main
成功獲取信號量id:327680
get sharemem successful!,shm_id is 3407884
------------ 共享內存段 --------------
鍵 shmid 擁有者 權限 字節 連接數 狀態
0x00000000 360448 xzj 600 16777216 2
0x00000000 458753 xzj 600 524288 2 目標
0x00000000 622594 xzj 600 524288 2 目標
0x00000000 294915 xzj 600 524288 2 目標
0x00000000 1245188 xzj 700 16472 2 目標
0x00000000 753669 xzj 600 67108864 2 目標
0x00000000 851974 xzj 600 524288 2 目標
0x00000000 1048583 xzj 600 524288 2 目標
0x00000000 1146888 xzj 600 524288 2 目標
0x00000000 1343497 xzj 600 524288 2 目標
0x00000000 1441802 xzj 600 524288 2 目標
0x00000000 1540107 xzj 600 524288 2 目標
0x6d010f3c 3407884 xzj 777 4096 0
0x00000000 2261005 xzj 600 524288 2 目標
0x00000000 2621454 xzj 700 161700 2 目標
0x00000000 2293775 xzj 600 67108864 2 目標
成功獲取共享內存id:3407884
red:共享內存映射成功!
------------ 共享內存段 --------------
鍵 shmid 擁有者 權限 字節 連接數 狀態
0x00000000 360448 xzj 600 16777216 2
0x00000000 458753 xzj 600 524288 2 目標
0x00000000 622594 xzj 600 524288 2 目標
0x00000000 294915 xzj 600 524288 2 目標
0x00000000 1245188 xzj 700 16472 2 目標
0x00000000 753669 xzj 600 67108864 2 目標
0x00000000 851974 xzj 600 524288 2 目標
0x00000000 1048583 xzj 600 524288 2 目標
0x00000000 1146888 xzj 600 524288 2 目標
0x00000000 1343497 xzj 600 524288 2 目標
0x00000000 1441802 xzj 600 524288 2 目標
0x00000000 1540107 xzj 600 524288 2 目標
0x6d010f3c 3407884 xzj 777 4096 1
0x00000000 2261005 xzj 600 524288 2 目標
0x00000000 2621454 xzj 700 161700 2 目標
0x00000000 2293775 xzj 600 67108864 2 目標
從共享內存里讀取的數據為:I am xzj,I am working in iauto!
write:共享內存解除映射成功!
delete shared_memory successful!
------------ 共享內存段 --------------
鍵 shmid 擁有者 權限 字節 連接數 狀態
0x00000000 360448 xzj 600 16777216 2
0x00000000 458753 xzj 600 524288 2 目標
0x00000000 622594 xzj 600 524288 2 目標
0x00000000 294915 xzj 600 524288 2 目標
0x00000000 1245188 xzj 700 16472 2 目標
0x00000000 753669 xzj 600 67108864 2 目標
0x00000000 851974 xzj 600 524288 2 目標
0x00000000 1048583 xzj 600 524288 2 目標
0x00000000 1146888 xzj 600 524288 2 目標
0x00000000 1343497 xzj 600 524288 2 目標
0x00000000 1441802 xzj 600 524288 2 目標
0x00000000 1540107 xzj 600 524288 2 目標
0x00000000 2261005 xzj 600 524288 2 目標
0x00000000 2621454 xzj 700 161700 2 目標
0x00000000 2293775 xzj 600 67108864 2 目標
xzj@xzj-VirtualBox:~/development_test/process/sharemem$
可以使用以下命令查看共享內存
查看共享內存:ipcs -m
刪除共享內存:ipcrm -m shm_id
消息隊列
三種系統V IPC:消息隊列、信號量以及共享內存(共享存儲器)之間有很多相似之處。每個內核中的 I P C結構(消息隊列、信號量或共享存儲段)都用一個非負整數的標識符( i d e n t i f i e r )加以引用。 無論何時創建I P C結構(調用m s g g e t、 s e m g e t或s h m g e t) ,都應指定一個關鍵字(k e y),關鍵字的數據類型由系統規定為 k e y _ t,通常在頭文件< s y s / t y p e s . h >中被規定為長整型。關鍵字由內核變換成標識符。
消息隊列的實現操作
① 創建或打開消息隊列。使用的函數是msgget(),這里創建的消息隊列的數量會受到系統消息隊列數量的限制。
② 添加消息。使用的函數是msgsnd(),它把消息添加到已打開的消息隊列末尾。
③ 讀取消息。使用的函數是msgrcv(),它把消息從消息隊列中取走,與FIFO不同的是,這里可以取走指定的某一條消息。
④ 控制消息隊列。使用的函數是msgctl(),它可以完成多項功能。
ftok函數
#include <sys/types.h>
#include <sys/ipc.h>
key_t ftok(const char *pathname, int proj_id);//“/home/linux” , 'a'
功能:生成一個key(鍵值)
msgget函數
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
int msgget(key_t key, int msgflg);
功能:創建或取得一個消息隊列對象
返回:消息隊列對象的id 同一個key得到同一個對象
格式:msgget(key,flag|mode);
flag:可以是0或者IPC_CREAT(不存在就創建)
mode:同文件權限一樣
msgsnd函數
int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
功能:將msgp消息寫入標識為msgid的消息隊列
msgp:
struct msgbuf {
long mtype; /* message type, must be > 0 */消息的類型必須>0
char mtext[1]; /* message data */長度隨意
};
msgsz:要發送的消息的大小 不包括消息的類型占用的4個字節
msgflg: 如果是0 當消息隊列為滿 msgsnd會阻塞
如果是IPC_NOWAIT 當消息隊列為滿時 不阻塞 立即返回
返回值:成功返回id 失敗返回-1
msgrcv函數
ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp,int msgflg);
功能:從標識符為msgid的消息隊列里接收一個指定類型的消息 並 存儲於msgp中 讀取后 把消息從消息隊列中刪除
msgtyp:為 0 表示無論什么類型 都可以接收
msgp:存放消息的結構體
msgsz:要接收的消息的大小 不包含消息類型占用的4字節
msgflg:如果是0 標識如果沒有指定類型的消息 就一直等待
如果是IPC_NOWAIT 則表示不等待
msgctl函數
int msgctl(int msqid, int cmd, struct msqid_ds *buf);
msgctl(msgid,IPC_RMID,NULL);//刪除消息隊列對象
可以使用以下命令查看消息隊列
查看共享內存:ipcs -q
刪除共享內存:ipcrm -q msqid
代碼實戰:一個進程從消息隊列里寫,一個進程從消息隊列里讀(阻塞讀)
頭文件:messagequeue.h
#ifndef MESSAGEQUEUE_H
#define MESSAGEQUEUE_H
#define BUFFER_SIZE 1024
struct message
{
long msg_type;
char msg_text[BUFFER_SIZE];
};
#endif // !MESSAGEQUEUE_H
從消息隊列中寫:msgsnd.cpp
/* msgsnd.cpp */
#include "messagequeue.h"
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
int main(int argc, char const *argv[])
{
int msgQueue_id;//消息隊列標識符
key_t key;//鍵值
struct message msg;
/* 根據不同的路徑和關鍵字產生標准的key */
if ((key = ftok(".", 'a')) == -1)
{
perror("ftok has a error:");
exit(1);
}
/* 創建消息隊列 */
if ((msgQueue_id = msgget(key, IPC_CREAT|0666)) == -1)
{
perror("msgget");
exit(1);
}
printf("打開或創建消息隊列成功,消息隊列ID為:%d\n",msgQueue_id);
while(1)
{
printf("請輸入你想輸入的消息(輸入exit結束):\n");
if ((fgets(msg.msg_text, BUFFER_SIZE, stdin)) == NULL)//輸入一行字符串
{
puts("no message");
exit(1);
}
msg.msg_type = getpid();
/* 添加消息到消息隊列 */
if ((msgsnd(msgQueue_id, &msg, strlen(msg.msg_text), 0)) < 0)
{
perror("message posted");
exit(1);
}
if (strncmp(msg.msg_text, "exit", 4) == 0)
{
break;
}
}
return 0;
}
從消息隊列里讀:msgrcv.cpp
/* msgrcv.cpp */
#include "messagequeue.h"
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
int main(int argc, char const *argv[])
{
int msgQueue_id;
key_t key;
struct message msg;
/* 根據不同的路徑和關鍵字產生標准的key */
if ((key = ftok(".", 'a')) == -1)
{
perror("ftok");
exit(1);
}
/* 創建消息隊列 */
if ((msgQueue_id = msgget(key, IPC_CREAT|0666)) == -1)
{
perror("msgget");
exit(1);
}
printf("打開或創建消息隊列成功,消息隊列ID為:%d\n",msgQueue_id);
do
{
/* 讀取消息隊列 */
memset(msg.msg_text, 0, BUFFER_SIZE);//清空消息數組
if (msgrcv(msgQueue_id, (void*)&msg, BUFFER_SIZE, 0, 0) < 0)
{
perror("msgrcv");
exit(1);
}
printf("來自進程id為:%ld 的消息為: %s", msg.msg_type, msg.msg_text);
} while(strncmp(msg.msg_text, "exit", 4));
/* 從系統內核中移走消息隊列 */
if ((msgctl(msgQueue_id, IPC_RMID, NULL)) < 0)
{
perror("msgctl");
exit(1);
}
return 0;
}
結果
讀進程
xzj@xzj-VirtualBox:~/development_test/process/messageQueue$ ./msgsnd_main
打開或創建消息隊列成功,消息隊列ID為:32768
請輸入你想輸入的消息(輸入exit結束):
my name is xzj,I am working in iauto!
請輸入你想輸入的消息(輸入exit結束):
wuhan is a beautiful city.
請輸入你想輸入的消息(輸入exit結束):
exit
寫進程
xzj@xzj-VirtualBox:~/development_test/process/messageQueue$ ./msgrcv_main
打開或創建消息隊列成功,消息隊列ID為:32768
來自進程id為:9611 的消息為: my name is xzj,I am working in iauto!
來自進程id為:9611 的消息為: wuhan is a beautiful city.
來自進程id為:9611 的消息為: exit
socket
工作流程:
1.服務端先用socket函數建立一個套接字,並調用listen函數,使服務端的這個端口和ip處於監聽狀態,等待客戶端的鏈接。
2.客戶端用socket函數建立一個套接字,設定遠程IP和端口,並調用connect函數。
3.服務端用accept函數來接受遠程計算機的鏈接,建立起與客戶之間的通信。
4.完成通信后,最后使用close函數關閉socket連接。