目錄
一、IPC通訊機制
1)傳統的通訊機制:
2)systemV 的通訊機制:
3)跨主機的通訊機制:
1、無名管道
1)無名管道的概念
2)無名管道的函數
3)無名管道通訊(親緣進程)
2、有名管道
1)有名管道的概念
2)有名管道的函數
?3)有名管道通訊(無親緣進程)
3、信號?
1)信號的概念
2)了解信號
3)信號的處理方式
4)signal(模擬中斷)
練習:捕獲信號 2)SIGHUP也就是ctrl c
?練習:捕獲信號 13)SIGPIPE 管道破裂
練習:使用signal回收僵尸進程
5)kill(給指定進程發送指定信號)
?6)raise(給自己發送信號)
7)alarm(秒級定時器)
?4、消息隊列
1)消息隊列原理
2)消息隊列的指令(ipcs)
3)ftok(創建密鑰)
?4)stat(查看文件路徑及屬性)
5)msgget(創建消息隊列)
6)msgsnd(發送信息)
7)msgrcv(接收信息)
8)msgctl(刪除、獲取、修改隊列信息)
一、IPC通訊機制
IPC:是指(inter process communction)進程間通訊機制
進程之間通訊可以通過文件實現,但是進程之間不清楚是什么時候開始通訊,因此實時操作性差。
由此 引出 IPC 通訊機制,他是通過內核空間共享實現,多個用戶空間獨立的進程之間通訊
1)傳統的通訊機制:
????????1. 無名管道文件 pipo
????????2. 有名管道文件 fifo
????????3. 信號 signal
2)systemV 的通訊機制:
????????1. 消息隊列 messge queue
????????2. 共享內存 shard memory
????????3. 信號燈集 semphore
3)跨主機的通訊機制:
????????嵌套字 socket
1、無名管道
1)無名管道的概念
1. 無名管道本質上是一個文件(管道文件 p ),但是存儲在內核中,普通文件存儲在硬盤上
2. 管道文件是特殊文件,只可以使用文件IO函數操作(open、write、read、close),lseek不允許使用
3. 管道需要滿足隊列的思想(先進先出),管道中的數據是一次性的,讀取數據后刪除數據
4. 管道屬于半雙工通訊機制
5. 當管道的讀寫端同時關閉,此時管道的內存自動釋放
6. 管道的大小位:64K
7. 無名管道只適用于有親緣關系的進程間的通訊
8. 如果打開寫端,關閉讀端,寫入字符會發生管道破裂(SIGPIPE)
2)無名管道的函數
格式:#include <unistd.h>int pipe(int pipefd[2]);
功能:創建無名管道
參數:int pipefd[2]:存儲管道兩端的文件描述符pipefd[0]:讀端的文件描述符pipefd[1]:寫端的文件描述符
返回值:成功返回0,失敗返回-1,跟新errno
3)無名管道通訊(親緣進程)
#include <25051head.h>
int main(int argc, const char *argv[]){//創建管道//pipefd[0]:讀 pipefd[1]:寫int pipefd[2];if(-1==pipe(pipefd)){ERRLOG("pipe error");}//無名管道用于存在親緣關系之間的進程通信,所以需要創建父子進程pid_t pid=fork();if(pid==-1){ERRLOG("fork error");}else if(0==pid) //子進程{close(pipefd[1]);//讀char buf[128]="";while(1){memset(buf,0,sizeof(buf));ssize_t res=read(pipefd[0],buf,sizeof(buf)-1);if(res==0){printf("讀取到文件的結尾..\n");break;}else if(res==-1){ERRLOG("read error");}if(strcmp(buf,"quit")==0){printf("子進程退出..\n");break;}//讀取成功printf("buf=[%s]\n",buf);}close(pipefd[0]);}else if(pid>0) //父進程{close(pipefd[0]);//寫while(1){ char buf[128]="";fgets(buf,sizeof(buf),stdin);buf[strlen(buf)-1]='\0';//把buf寫入到管道文件中write(pipefd[1],buf,strlen(buf));if(strcmp(buf,"quit")==0){printf("父進程退出..\n");break;}}close(pipefd[1]);}return 0;
}
2、有名管道
1)有名管道的概念
有名管道和無名管道特點一致,只有一點不同:有名管道可用于無親緣關系的進程間通訊
2)有名管道的函數
格式:#include <sys/types.h>#include <sys/stat.h>int mkfifo(const char *pathname, mode_t mode);功能:創建有名管道
參數:const char *pathname:創建有名管道的文件名以及路徑mode_t mode:有名管道文件的權限 0664 0777
返回值:成功返回0,失敗返回-1.跟新errno指令:mkfifo 路徑及文件名
?3)有名管道通訊(無親緣進程)
3、信號?
1)信號的概念
信號是軟件層對中斷的一種模擬
信號是異步通訊模式
異步是指任務和任務之間沒有聯系,按照CPU的時間片輪詢機制訪問
2)了解信號
信號可以通過指令 kill - l 查看
1? ~ 31:標準信號,當多次觸發同一信號,內核只處理一次
34~ 64:實時信號,當多次觸發同一信號是,會把信號存儲到隊列中,按照隊列思想,逐個處理
編號 | 信號名 | 含義 | 快捷方式/觸發方式 |
1 | SIGHUP | 終端掛斷(如SSH連接斷開、守護進程重載配置) | kill -1 或系統事件觸發 |
2 | SIGINT | 終端中斷請求(用戶主動終止進程) | ctrl c |
3 | SIGQUIT | 終端退出請求(類似SIGINT,生成核心轉儲用于調試) | Ctrl+\ |
4 | SIGILL | 非法指令(執行無效的CPU指令) | 程序錯誤或硬件異常觸發 |
5 | SIGTRAP | 斷點陷阱(調試器捕獲斷點或單步執行) | 調試器觸發 |
6 | SIGABRT | 程序異常終止(由abort()函數觸發) | assert()失敗或 abort()調用 |
7 | SIGBUS | 總線錯誤(非法內存訪問,如未對齊的內存操作) | 硬件或內存錯誤觸發 |
8 | SIGFPE | 浮點異常(如除以零、溢出) | 算術運算錯誤觸發 |
9 | SIGKILL | 強制終止進程(不可捕獲或忽略) | kill -9 |
10 | SIGUSR1 | 用戶自定義信號(用途由程序定義,如重開日志) | kill -10 |
11 | SIGSEGV | 段錯誤(非法內存訪問,如空指針解引用) | 內存訪問錯誤觸發 |
12 | SIGUSR2 | 用戶自定義信號2(用途由程序定義) | kill -12 |
13 | SIGPIPE | 管道破裂(向無讀端的管道寫數據) | 管道操作錯誤觸發 |
14 | SIGALRM | 定時器超時(由alarm()或setitimer()設置) | 定時器到期觸發 |
15 | SIGTERM | 終止請求(允許程序優雅退出,默認kill命令發送的信號) | kill -15? 或? kill (無參數) |
16 | SIGSTKFLT | 協處理器棧錯誤(少見,部分系統未實現) | 硬件協處理器錯誤觸發 |
17 | SIGCHLD | 子進程狀態變更(子進程終止或暫停) | 子進程事件觸發 |
18 | SIGCONT | 繼續執行進程(恢復被暫停的進程) | fg 命令或 kill -18 |
19 | SIGSTOP | 暫停進程(不可捕獲或忽略) | Ctrl+Z (部分系統行為) |
20 | SIGTSTP | 終端暫停請求(后臺進程嘗試終端輸入/輸出) | Ctrl+Z |
23 | SIGURG | 緊急數據到達(如TCP帶外數據) | 網絡數據包觸發 |
24 | SIGXCPU | 超出CPU時間限制(資源限制觸發) | setrlimit() 設置超限觸發 |
25 | SIGXFSZ | 超出文件大小限制(如寫入超過 ulimit 限制的文件) | 文件操作超限觸發 |
26 | SIGVTALRM | 虛擬定時器超時(基于進程的虛擬時間) | setitimer(ITIMER_VIRTUAL) 觸發 |
27 | SIGPROF | 性能分析定時器超時(統計CPU時間) | setitimer(ITIMER_PROF) 觸發 |
28 | SIGWINCH | 終端窗口大小變化(如調整終端窗口) | 終端尺寸變更事件觸發 |
29 | SIGIO | 異步I/O事件(文件描述符準備就緒) | I/O事件觸發(需配置fcntl()) |
30 | SIGPWR | 電源故障(系統關機前通知進程) | UPS或電源管理事件觸發 |
31 | SIGSYS | 無效系統調用(執行不存在或參數錯誤的系統調用) | 非法系統調用觸發 |
34 | SIGRTMIN | 實時信號起始編號(自定義用途,范圍: SIGRTMIN ~ SIGRTMAX,通常34~64) | kill -34 或更高編號 |
3)信號的處理方式
信號屏蔽:在執行信號處理時,連續觸發同一個信號,該信號只處理一次,這個行為我們稱之為信號屏蔽,但是可以觸發其他信號。
1. 執行默認操作:當觸發信號時,執行默認的信號處理函數
2. 忽略信號:當觸發信號時,忽略該信號不做處理
3. 捕獲信號:當觸發信號時,執行我們想讓他執行的函數
4)signal(模擬中斷)
格式:#include <signal.h>typedef void (*sighandler_t)(int);sighandler_t signal(int signum, sighandler_t handler);功能:信號處理,接收到指定信號,并選擇處理方式(執行默認函數,忽略,捕獲)
參數:int signum:指定信號,接收到該信號后,執行操作(可以填信號編號,也可以填信號(大寫字母))sighandler_t handler: 有以下指定參數SIG_IGN: 忽略信號SIG_DFL: 執行默認操作填函數名(函數首地址): 觸發執行該函數注意:9)SIGKILL 和 19)SIGSTOP 不允許捕獲和忽略返回值:成功返回上一個信號處理,失敗返回SIG_ERR 更新 errno
練習:捕獲信號 2)SIGHUP也就是ctrl c
?練習:捕獲信號 13)SIGPIPE 管道破裂
練習:使用signal回收僵尸進程
5)kill(給指定進程發送指定信號)
格式:#include <signal.h>int kill(pid_t pid, int sig);功能:發送信號給進程或者進程組
參數:pid_t pid:>0: 把信號發送給進程等于pid的進程=0:把信號發送給當前進程組下的所有有權發送的進程=-1:把信號發送給系統的所有進程,除了沒有權限發送的init進程<0:但不是-1. 先取絕對值,進程組id,把信號發送給改進程組下有權發送的進程int sig:信號的編號 或者 宏0:會做錯誤檢測,不發送信號
返回值:成功返回0,失敗返回-1.跟新errno
?6)raise(給自己發送信號)
格式:#include <signal.h>int raise(int sig);功能:給調用者進程發送信號(自殺)
參數:int sig表示發送的信號編號 或在宏
返回值:成功返回0,失敗返回非0
7)alarm(秒級定時器)
格式:#include <unistd.h>unsigned int alarm(unsigned int seconds);完全等價于:alarm--->kill(getpid(),14) 14) SIGALRM
功能:發送一個時鐘信號
參數:unsigned int seconds:幾秒鬧鐘響
返回值:返回上一次鬧鐘沒有觸發的剩余秒數,如果沒有則返回0
?可以將alarm放在執行函數內,重復觸發alarm。
?4、消息隊列
1)消息隊列原理
消息隊列在內核中申請一片空間,把信息打包成結點存儲在內核緩沖區中,進程通過訪問內核緩沖區中結點實現通訊
1. 把數據打包成結點(數據類型、數據內容)
2. 需滿足隊列思想(先進先出),即使按類型讀取數據,也要滿足隊列思想
3. 在讀取消息隊列的數據后,數據默認會刪除
4. 消息隊列屬于全雙工通訊機制
5. 消息隊列不會隨著進程的結束而結束,需要手動刪除 或者 電腦重啟?
2)消息隊列的指令(ipcs)
ipcs 同時查看消息隊列、共享內存段、信號量數組
ipcs -q 只查看消息隊列
ipcs -q msgid 刪除消息隊列
3)ftok(創建密鑰)
格式:#include <sys/types.h>#include <sys/ipc.h>key_t ftok(const char *pathname, int proj_id);功能:把pathname 和proj_id轉換為秘鑰的,被msgget shmget semget使用
參數:const char *pathname:路徑以及文件名 (隨便但是需要存在)int proj_id:可以寫整數,字符(隨便)
返回值:成功返回秘鑰,失敗返回-1,跟新errno,注意秘鑰和stat有關 key=proj_id(低8位)+設備號(低8位)+inode(低16位)
?4)stat(查看文件路徑及屬性)
#include <sys/types.h>#include <sys/stat.h>#include <unistd.h>int stat(const char *pathname, struct stat *statbuf);int lstat(int fd, struct stat *statbuf);
功能:stat查看文件以及路徑的屬性的,stat不可以查看連接文件,lstat函數可以查看連接文件
參數:const char *pathname:查看的文件以及路徑struct stat *statbuf:該結構體指針重中存儲 執行文件的屬性信息struct stat {dev_t st_dev; /* 設備號 */ino_t st_ino; /* inode號 */mode_t st_mode; /* 文件類型和權限 */nlink_t st_nlink; /* 硬連接數 */uid_t st_uid; /* 用戶id */gid_t st_gid; /* 組id */dev_t st_rdev; /* 設備id*/off_t st_size; /* 總字節大小 */blksize_t st_blksize; /* Block size for filesystem I/O */blkcnt_t st_blocks; /* Number of 512B blocks allocated *//* Since Linux 2.6, the kernel supports nanosecondprecision for the following timestamp fields.For the details before Linux 2.6, see NOTES. */struct timespec st_atim; /* 最后一次訪問的時間 */struct timespec st_mtim; /* 最后一次修改的事件 */struct timespec st_ctim; /* 最后一次狀態值修改的之間 */#define st_atime st_atim.tv_sec /* Backward compatibility */#define st_mtime st_mtim.tv_sec#define st_ctime st_ctim.tv_sec};
5)msgget(創建消息隊列)
格式:#include <sys/types.h>#include <sys/ipc.h>#include <sys/msg.h>int msgget(key_t key, int msgflg);功能:創建消息隊列的
參數:
key_t key:秘鑰,ftok函數的返回值
int msgflg:實際怎么IPC_CREAT | 0664 或者 IPC_CREAT|IPC_EXCL|0664IPC_CREAT:如果消息隊列不存在,則創建消息隊列,存在不會報錯IPC_CREAT|IPC_EXCL:如果細隊列存在則報錯EEXIST,不存在則創建
返回值:成功返回消息隊列id,失敗返回-1,。跟新errno
6)msgsnd(發送信息)
格式: #include <sys/types.h>#include <sys/ipc.h>#include <sys/msg.h>int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);功能:發送消息包的
參數:int msqid:消息隊列idconst void *msgp: 發送的消息包,例如下面的結構體struct msgbuf {long mtype; /* 消息的類型, 必須> 0 */char mtext[1]; /* 消息的內容 */};//發送一個字符串struct A {long a; /* 消息的類型, 必須> 0 */char str[128]; /* 消息的內容 */};//發送一個整數struct B {long b; /* 消息的類型, 必須> 0 */int num; /* 消息的內容 */};//發送一個學生的信息struct V {long c; /* 消息的類型, 必須> 0 */char name[128]; /* 消息的內容 */int age;float high;};size_t msgsz:消息內容的細節大小 sizeof(struct msgbuf)-sizeof(long)int msgflg :0:阻塞 當消息滿后則阻塞IPC_NOWAIT:非阻塞函數,當消息隊列滿不會阻塞,但是報錯EAGAIN
返回值:成功返回0,失敗返回-1
7)msgrcv(接收信息)
格式:#include <sys/types.h>#include <sys/ipc.h>#include <sys/msg.h>ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp,int msgflg);
功能:接收消息隊列的數據包
參數:int msqid:消息隊列idconst void *msgp: 發送的消息包,例如下面的結構體struct msgbuf {long mtype; /* 消息的類型, 必須> 0 */char mtext[1]; /* 消息的內容 */};//發送一個字符串struct A {long a; /* 消息的類型, 必須> 0 */char str[128]; /* 消息的內容 */};//發送一個整數struct B {long b; /* 消息的類型, 必須> 0 */int num; /* 消息的內容 */};//發送一個學生的信息struct V {long c; /* 消息的類型, 必須> 0 */char name[128]; /* 消息的內容 */int age;float high;};size_t msgsz:消息內容的細節大小 sizeof(struct msgbuf)-sizeof(long)long msgtyp:指定消息隊列的類型=0:讀取消息隊列的第一條信息 >0: 讀取類型等于msgtyp的第一條信息如果msgflag指定MSG_EXCEPT,讀取不等于msgtype的第一條信息<0:讀取小于等于msgtype絕對值的第一條信息int msgflg :0:阻塞 當消息滿后則阻塞IPC_NOWAIT:非阻塞函數,當消息隊列滿不會阻塞,但是報錯EAGAIN返回值:成功返回消息內容的字節大小,失敗返回-1 ,跟新errno1:aaa 2:bbb 6:cccc 2:dddd 4:eeee 3:ffff
msgtyp=0
msgrcv(msqid, &buf, sizeof(buf)-sizeof(long), 0,0); //aaa
msgrcv(msqid, &buf, sizeof(buf)-sizeof(long), 0,0); //bbb
msgrcv(msqid, &buf, sizeof(buf)-sizeof(long), 0,0); //ccc
msgrcv(msqid, &buf, sizeof(buf)-sizeof(long), 0,0); //ddd
msgtype>0
msgrcv(msqid, &buf, sizeof(buf)-sizeof(long), 2,0);//bbb
msgrcv(msqid, &buf, sizeof(buf)-sizeof(long), 2,0);//dddd
msgrcv(msqid, &buf, sizeof(buf)-sizeof(long), 2,0);//阻塞
msgtype>0 msgflg指定MSG_EXCEPT
msgrcv(msqid, &buf, sizeof(buf)-sizeof(long), 2,0|MSG_EXCEPT); //aaa
msgrcv(msqid, &buf, sizeof(buf)-sizeof(long), 2,0|MSG_EXCEPT); //ccc
msgrcv(msqid, &buf, sizeof(buf)-sizeof(long), 2,0|MSG_EXCEPT); //eeee
msgrcv(msqid, &buf, sizeof(buf)-sizeof(long), 2,0|MSG_EXCEPT); //ffff
msgrcv(msqid, &buf, sizeof(buf)-sizeof(long), 2,0|MSG_EXCEPT); //阻塞
msgtype<0
msgrcv(msqid, &buf, sizeof(buf)-sizeof(long), -4,0); //aaa
msgrcv(msqid, &buf, sizeof(buf)-sizeof(long), -4,0); //ccc
msgrcv(msqid, &buf, sizeof(buf)-sizeof(long), -4,0); //ddd
msgrcv(msqid, &buf, sizeof(buf)-sizeof(long), -4,0); //fff
msgrcv(msqid, &buf, sizeof(buf)-sizeof(long), -4,0); //eeee
msgrcv(msqid, &buf, sizeof(buf)-sizeof(long), -4,0); //阻塞
8)msgctl(刪除、獲取、修改隊列信息)
格式:#include <sys/types.h>#include <sys/ipc.h>#include <sys/msg.h>int msgctl(int msqid, int cmd, struct msqid_ds *buf);
功能:消息隊列控制(刪除,獲取信息,修改消息隊列的信息)
參數:int msqid:消息的idint cmd:IPC_STAT:獲取消息隊列內核數據的IPC_SET:修改消息隊列數據IPC_RMID:刪除消息隊列 ,第三個填 NULLstruct msqid_ds *buf,該指針存儲獲取以及修改對應內核的數據struct msqid_ds {struct ipc_perm msg_perm; /* Ownership and permissions */time_t msg_stime; /* Time of last msgsnd(2) */time_t msg_rtime; /* Time of last msgrcv(2) */time_t msg_ctime; /* Time of last change */unsigned long __msg_cbytes; /* Current number of bytes inqueue (nonstandard) */msgqnum_t msg_qnum; /* Current number of messagesin queue */msglen_t msg_qbytes; /* Maximum number of bytesallowed in queue */pid_t msg_lspid; /* PID of last msgsnd(2) */pid_t msg_lrpid; /* PID of last msgrcv(2) */};struct ipc_perm {key_t __key; /* Key supplied to msgget(2) */uid_t uid; /* Effective UID of owner */gid_t gid; /* Effective GID of owner */uid_t cuid; /* Effective UID of creator */gid_t cgid; /* Effective GID of creator */unsigned short mode; /* Permissions */unsigned short __seq; /* Sequence number */};
返回值:
如果指定IPC_STAT IPC_SET IPC_RMID 成功返回0,失敗返回-1,跟新errno例如:刪除消息隊列
msgctl(msgid,IPC_RMID,NULL)
例如:獲取消息隊列的權限
struct msqid_ds buf;
msgctl(msgid,IPC_STAT,&buf);
printf("權限:%ld\n",buf.msg_perm.mode);
例如:修改消息隊列的權限0777
1.先獲取原來的屬性
struct msqid_ds buf;
msgctl(msgid,IPC_STAT,&buf);
2.只修改權限
buf.msg_perm.mode=0777;
msgctl(msgid,IPC_SET,&buf);
printf("權限:%ld\n",buf.msg_perm.mode);
練習: 從信息隊列中讀取字符