Linux程序相關API及多程序間通訊
一、相關API
1、程序的建立fork() #include <unistd.h> pid_t fork(void); pid_t vfork(void); 返回值: > 0 父程序 parent process ==0 子程序 child process < 0(-1) 失敗 fork的特點: 第一個特點: 一次呼叫,兩次返回,返回大於0(值就是子程序的id號)表示父程序,等於0表示子程序 子程序會複製父程序的所有資源(程式碼段,資料段) 第二個特點:父程序總共分成三個部分 第一個部分在fork之前 第二個部分fork成功之後,在id>0情況下的那部分程式碼 第三個部分fork後面的 總結: 第一點:fork()的套路 if(>0){ 父程序程式碼 }else if(==0){ 程式設計師建立子程序需要併發執行的任務程式碼 }else{ 錯誤 }
第二點:vfork建立的子程序共享父程序的資源,vfork建立的子程序一定優先於父程序執行 2、程序的退出跟回收exit()/wait() #include <stdlib.h> void exit(int status); //退出程序的同時重新整理緩衝區 void _exit(int status);//退出程序的時候不重新整理緩衝區 引數:status -->程序退出時候的值 對比return: 區別一:return是C語言中關鍵字,exit()是函式 區別二:return是返回函式呼叫的結果,exit()是結束整個程序 #include <sys/wait.h> pid_t wait(int *stat_loc);--->收子程序結束返回值(收屍) 返回值:成功 返回值回收到的那個子程序的ID,失敗 -1 引數:stat_loc -->用來存放程序退出時候的狀態資訊 不是存放退出值,退出值僅僅只是狀態資訊中的一部分 特點:會讓父程序一直阻塞,直到成功回收到子程序為止 pid_t waitpid(pid_t pid, int *stat_loc, int options); 返回值:成功 返回值回收到的那個子程序的ID,失敗 -1 引數: pid --> 小於-1 waitpid(-1200,&status,options);回收程序組id是1200的其中一個 等於-1 waitpid(-1,&status,options);回收任意一個子程序(不要求同組) 等於0 waitpid(0,&status,options); 回收本程序組中任意一個子程序 大於0 waitpid(1200,&status,options); 指定回收id是1200的子程序 stat_loc -->跟wait一樣 options -->WN0HANG 非阻塞等待,等得到就等,等不到就直接退出 0 阻塞等待 3、獲取當前程序的id以及父程序的id #include <sys/types.h> #include <unistd.h> pid_t getpid(void); //獲取子程序ID pid_t getppid(void); //獲取父程序ID gid_t getgid(void); //獲取程序組ID 4、使用函式執行shell命令 #include <stdlib.h> #include <unistd.h> int system(const char *command); 返回值:失敗 -1 引數: command -->你要執行的shell命令或者你要執行程式完整的命令名 int execl(const char *path, const char *arg, ...); 引數: path -->你要執行的程式/命令所在的路徑 arg -->你要執行的命令/程式 int execlp(const char *file, const char *arg, ...); int execle(const char *path, const char *arg,..., char * const envp[]); int execv(const char *path, char *const argv[]); int execvp(const char *file, char *const argv[]); int execvpe(const char *file, char *const argv[],char *const envp[]); 引數: file-->即將被載入執行的檔名 argv-->儲存即將被執行的引數的陣列 envp-->使用者自定義的環境變數陣列 總結: l -->引數以列表的形式逐一列舉 e -->引數中可以設定環境變數 v -->引數用一個指標陣列存放 p -->傳遞程式/命令的名字
二、程序間通訊
(一)無名管道和有名管道 1、建立無名管道 #include <unistd.h> int pipe(int fildes[2]); 返回值:成功 0 失敗 -1 引數: fildes[2] -->存放的是兩個檔案描述符 fildes[0]讀端的檔案描述符 fildes[1]寫端的檔案描述符 特點: 有固定的讀端(fd[0])跟寫端(fd[1]) 當管道中沒有資料可以讀的時候,呼叫read會阻塞 只能用於具有血緣關係的程序之間(父子程序,兄弟程序之間) 2、建立有名管道 #include <sys/types.h> #include <sys/stat.h> int mkfifo(const char *pathname, mode_t mode); 返回值:成功 0 失敗 -1 引數: pathname -->你要建立的有名管道的名字(帶路徑) mode -->0777 特點: 有名字,生成一個管道檔案,用於通訊 當管道中沒有資料可以讀的時候,呼叫read會阻塞 任意兩個不同程序之間都能通訊 3、判斷檔案是否存在 #include <unistd.h> int access(const char *path, int amode); 功能:判斷一個檔案是否存在,引數用來判斷檔案是否可讀、是否可寫、是否可執行 返回值:符合你要求的判斷條件返回0 否則 -1 引數: path -->檔案的路徑 amode -->R_OK, W_OK, X_OK ,F_OK (二)訊號:
作用是當一個程序傳送訊號給另外一個程序的時候,可以通過該訊號去控制另外一個程序執行程式設計師想幹的事情 1、傳送訊號 #include <signal.h> int kill(pid_t pid, int sig); 引數: pid -->你要傳送訊號的那個程序的id sig -->你要傳送的訊號 int sigqueue(pid_t pid, int sig, const union sigval value); 引數:union sigval { int sival_int; void *sival_ptr; //void *萬能指標 };//存放你想傳送的額外資料 跟kill的區別:sigqueue買一送一(傳送訊號的同時可以額外發送其他資料給到程序) 2、捕捉訊號並改變訊號的響應動作 #include <signal.h> void (*signal(int sig, void (*func)(int)))(int); 返回值:最近一次呼叫時第二個引數的值(函式指標) 引數:sig -->你想要捕捉的訊號 第二個引數有三種情況: 情況一: void (*func)(int) -->函式指標,你想要改變的訊號動作就靠該函式實現 情況二: SIG_DFL -->按照訊號預設的動作響應 情況三: SIG_IGN -->忽略訊號,收到訊號之後不做任何響應,直接捨棄 注意:在所有的訊號中有兩個訊號是既不能改變預設動作也不能忽略,SIGKILL和SIGSTOP int sigaction(int sig, const struct sigaction *restrict act,struct sigaction *restrict oact); 引數: struct sigaction { void(*)(int) sa_handler //跟signal中函式指標一模一樣 sigset_t sa_mask //訊號阻塞掩碼?? int sa_flags //設定0選擇sa_handler //設定SA_SIGINFO表示選擇sa_sigaction void(*)(int,siginfo_t *,void *) sa_sigaction//另外一個訊號響應函式,接收額外資料 } void(*)(int,siginfo_t *,void *) siginfo_t { si_int -->存放union sigval裡面的sival_int si_ptr -->存放union sigval裡面的sival_ptr si_pid -->存放傳送訊號的那個程序的id } oact-->原有訊號的處理引數,一般為NULL 3、其它簡單函式 #include <signal.h> int pause(void);//阻塞當前程序等待訊號到來 int raise(int sig);//自己給自己傳送訊號 unsigned alarm(unsigned seconds);//定時器,alarm(5);過5秒之後自己給自己傳送SIGALRM 4、訊號的阻塞或遮蔽 #include <signal.h> int sigprocmask(int how, const sigset_t *restrict set,sigset_t *restrict oset);//設定訊號阻塞的函式 返回值:成功 0 失敗 -1 引數:how -->SIG_BLOCK //設定阻塞 將set對應訊號新增到原本的訊號集合中 SIG_SETMASK //設定阻塞 用set替換原本的訊號集合中的訊號 SIG_UNBLOCK //解除阻塞 sigset_t --> 系統定義的一種變數型別,專門用來存放你想要阻塞的訊號 稱之為訊號阻塞掩碼集 操作訊號阻塞掩碼集合的函式 int sigemptyset(sigset_t *set); //清空掩碼集 int sigfillset(sigset_t *set); //將所有的linux訊號新增到集合中 int sigaddset(sigset_t *set, int signum);//將具體的某個訊號新增到集合 int sigdelset(sigset_t *set, int signum);//將具體的某個訊號從集合刪除 int sigismember(const sigset_t *set, int signum);//判斷某個訊號在不在集合 返回1是成員 返回0不是成員 注意:訊號設定阻塞僅僅只是將訊號暫時掛起,訊號依然存在(等到解除阻塞又能重新響應)
********************************************************************************************************************************************** 《system-V IPC通訊 》:指的就是共享記憶體,訊息佇列,訊號量 linux命令: ipcs -s 檢視當前系統所有的訊號量 ipcs -m 檢視當前系統所有的共享記憶體 ipcs -q 檢視當前系統所有的訊息佇列 ipcs -a 檢視當前系統所有的IPC物件 ipcrm -s 訊號量id 刪除訊號量 ipcrm -m 共享記憶體id 刪除共享記憶體 ipcrm -q 訊息佇列id 刪除訊息佇列 (三)訊號量:用來協調多個程序對應共享資源的訪問 特點:當訊號量的值為0,你還想p操作,會阻塞當前程序 訊號量的值是不可能為負數的 v操作永遠不會阻塞 1、建立訊號量 #include <sys/sem.h> #include <sys/ipc.h> int semget(key_t key, int nsems, int semflg); 返回值:成功 訊號量的id 失敗 -1 引數:key -->鍵值,確保唯一性 產生鍵值兩種方法 第一種:自己隨便寫一個(正整數) 第二種:使用系統提供的ftok()生成一個鍵值 #include <sys/ipc.h> key_t ftok(const char *pathname, int proj_id); 返回值:成功 返回鍵值 失敗 -1 引數:pathname -->合法的linux路徑 proj_id -->隨便寫個整數 ftok(".",200); ftok(".",199); nsems -->你打算建立多少個訊號量 semflg -->IPC_CREAT訊號量不存在則建立 IPC_EXCL訊號量已存在則報錯 0777 訊號量的訪問許可權 2、獲取或設定訊號量的相關屬性 #include <sys/sem.h> #include <sys/ipc.h> int semctl(int semid, int semnum, int cmd, ...); 引數: semid -->訊號量的ID,semget的返回值 semnum -->訊號量的序號,從0開始 cmd -->GETVAL //獲取訊號量值 int value=semctl(id,0,GETVAL);返回值給value SETVAL //設定訊號量值 semctl(id,0,SETVAL,10);//將第一個訊號量值設定為10 IPC_RMID //刪除訊號量 3、訊號量的PV操作 #include <sys/sem.h> #include <sys/ipc.h> int semop(int semid, struct sembuf *sops, size_t nsops); 返回值: 引數:struct sembuf { short sem_num 訊號量的序號 short sem_op 決定你究竟是想P操作還是V操作,負數P操作,正數V操作 short sem_flg SEM_UNDO(操作完訊號量之後,恢復成原本值) } nsops -->訊號量struct sembuf個數 (四)共享記憶體:效率最高的IPC 1、申請共享記憶體 #include <sys/shm.h> #include <sys/ipc.h> int shmget(key_t key, size_t size, int shmflg); 返回值:成功 共享記憶體的ID 失敗 -1 引數:size -->你打算申請多少的記憶體,位元組,一般設定成512的整數倍 shmflg->IPC_CREAT共享記憶體不存在則建立 IPC_EXCL共享記憶體已存在則報錯 0777 共享記憶體的訪問許可權 2、對共享記憶體進行對映或解除對映 #include <sys/shm.h> #include <sys/ipc.h> void *shmat(int shmid, const void *shmaddr, int shmflg); int shmdt(const void *shmaddr); 引數:shmid-->共享記憶體id shmaddr -->一般為NULL shmflg -->一般為0 返回值:成功返回共享記憶體的首地址 失敗 -1 3、獲取、設定共享記憶體相關屬性或刪除共享記憶體 #include <sys/shm.h> #include <sys/ipc.h> int shmctl(int shmid, int cmd, struct shmid_ds *buf); 引數:cmd -->IPC_STAT //獲取共享記憶體的屬性資訊 IPC_SET //設定共享記憶體的屬性資訊 IPC_RMID //刪除共享記憶體 struct shmid_ds -->用來存放共享記憶體的屬性資訊 (五)訊息佇列 1、建立訊息佇列 #include <sys/ipc.h> #include <sys/msg.h> int msgget(key_t key, int msgflg); 引數:shmflg->IPC_CREAT訊息佇列不存在則建立 IPC_EXCL訊息佇列已存在則報錯 0777 訊息佇列的訪問許可權 2、收發資訊 #include <sys/ipc.h> #include <sys/msg.h> int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg); 返回值:成功 0 失敗 -1 引數:msgflg -->預設設定0 //阻塞 IPC_NOWAIT //非阻塞 msgp--->要傳送資料的儲存區域指標 msgsz-->要傳送資料的大小 ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp,int msgflg); 引數:msgtyp -->你要接收的資訊型別 msgp--->要接收資料的儲存區域指標 msgsz-->要接收資料的大小 重點:傳送訊息的格式是有要求的,要求使用者自定資料型別 struct msgbuf { long msgtype; //表明訊息型別 char truemsg[50];//真實的資訊內容 }; struct singlelist { int num;//真實的資料 char buf[10]; //next指標 }; 3、刪除訊息佇列 #include <sys/ipc.h> #include <sys/msg.h> int msgctl(int msqid, int cmd, struct msqid_ds *buf); 引數:cmd-->IPC_STAT 獲取MSG的資訊 IPC_SET 設定MSG的資訊 IPC_RMID 刪除MSG buf-->存放資訊結構體緩衝區