回顧:
進程間通訊方式:
信號,管道
消息隊列,共享內存,信號量
sokcet
信號:
本質就是軟中斷
signal(信號,函數指針);
void func(int);
kill(pid,signo);
raise(signo);
alarm(seconds);
pause();
kill -9 PID
--------------------------------
管道:
1.基本概念:
管道本質上仍是以文件做爲通訊的媒介,該文件比較特殊,叫管道文件
管道分爲兩類:
無名管道(pipe)和有名管道(fifo)
有名管道:由程序員手動建立,實現任意兩個進程之間的通訊
無名管道:調用系統函數建立,實現父子進程間的通訊。
無名管道的建立與關閉:
管道是基於文件描述符的通訊方式,當一個管道創建時,它會建立兩個文件描述符fd[0]和fd[1],其中fd[0]固定用於讀管道,fd[1]固定寫管道,這樣構成一個半雙工的通道。
管道關閉時,只需將這兩個文件描述符關閉便可,使用close函數。
管道建立函數:
int pipe(int pipefd[2]);
功能:用於內核中建立無名管道
pipefd:整形數組,用來存放管道的讀寫文件描述符
pipefd[0]:用來存放讀端的文件描述符
pipefd[1]:用來存放寫端的文件描述符
返回:
成功返回0
失敗返回-1,errno被設置
管道讀寫說明:
用pipe()這個函數建立的管道兩端處於同一進程之中,所以,在實際中沒有太大意義。
一般的作法:先建立一個管道,再經過fork()函數建立一個子進程,子進程會繼承父進程所建立的管道,爲了實現父子進程間的通訊,須要把無關的讀端或寫端的文件描述符關閉。
管道的特色:
pipe建立的管道是阻塞方式的。
去讀一個管道,若是管道里沒有數據,則阻塞,直到有數據可讀
往管道中寫數據,若是管道不可寫,則阻塞,直到文件可寫。
數據寫到管道的寫端,內核會把這些數據緩存,直到有進程來讀。
數據一旦被讀走,就沒有了。
---------------------
有名管道
爲了克服無名管道的缺點,提出了有名管道。
該管道用於不一樣進程之間的通訊,它提供了一個路徑名與之關聯,以FIFO的文件形式存在於文件系統
在創建了管道以後,兩個進程就能夠把它看成普通文件同樣進行讀寫操做,不支持lseek定位操做
要注意的是,有名管道的名字存在於文件系統中,內容放在內存中。
mkfifo f1.pipe
1.有名管道的建立
int mkfifo(const char *pathname, mode_t mode);
pathname:是一個普通的路徑名,也就是建立後FIFO文件的名字
mode:與打開普通文件open()函數中的mode參數相同
1.0666,0777,0644...
2.S_IRUSR S_IWGRP S_IXOTH...
返回值:成功返回0
失敗返回-1,errno被設置
注:若是mkfifo的第一個參數是個已經存在的路徑名時,會返回EEXIST錯誤
因此,通常最典型的寫法是調用代碼首先檢查是否返回該錯誤,若是確實返回該錯誤,那麼只須要調用打開FIFO的函數就能夠了。
2.有名管道的打開規則:
有名管道比無名管道多了一個打開操做:open()
--------------------------------------
共享內存:
共享內存是以一塊內存做爲IPC交互的媒介,這塊內存由內核維護和管理,容許其它進程映射,共享內存效率是最高的。
1.共享內存的操做流程:
1.建立/獲取它共享內存
2.映射共享內存,即把指定的共享內存映射到進程的地址空間用於訪問。
3.正常使用
4.解除映射
5.若是確保再也不使用,刪除共享內存對像
2.相關的API函數
1.shmget函數
#include <sys/ipc.h>
#include <sys/shm.h>
int shmget(key_t key, size_t size, int shmflg);
功能:主要用於建立/獲取共享內存段
第一個參數:key,標識共享內存的鍵值(就像文件的標識是文件名)
第二個參數:要創建的共享內存的長度
第三個參數:有效的標誌有IPC_CREAT和IPC_EXCL,與open()函數的O_CREAT與O_EXCL至關。
IPC_CREAT:若是共享內存不存在,則建立一個
IPC_EXCL:與IPC_CREAT搭配使用,若是存在,建立失敗
返回值:成功返回一個shmid(相似於打開/建立一個文件得到文件描述符同樣)
失敗返回-1,errno被設置
注:
當新建立共享內存時,通常狀況下第三個參數是:0666 | IPC_CREAT | IPC_EXCL
2.ftok()函數
key_t ftok(const char *pathname, int proj_id);
功能:根據文件路徑和項目編號生成key值
第一個參數:字符串形式的文件路徑,要求文件必須存在,且可訪問。
第二個參數:整型的項目編號,要求必須是非0,低8位被使用,通常寫一個字符代替
返回值:成功返回key_t類型的key值
失敗返回-1,errno被設置程序員
3.shmat函數
void *shmat(int shmid, const void *shmaddr, int shmflg);
功能:用於映射共享內存到進程的地址空間
第一個參數:shmid,共享內存的ID,shmget()函數的返回值
第二個參數:shmaddr,將共享內存映射到指定的地址,給NULL/0由系統指定
第三個參數:shmflg,默認給0便可,表示共享內存可讀可寫
返回值:成功返回共享內存的映射地址
失敗返回-1,errno被設置數組
4.shmdt函數
int shmdt(const void *shmaddr);
功能:用於取消共享內存與用戶進程的映射
參數:shmaddr:就是shmat的返回值
5.shmctl函數
int shmctl(int shmid, int cmd, struct shmid_ds *buf);
功能:用於對指定的共享內存執行指定的操做
第一個參數:shmid共享內存的ID,即shmget()返回值
第二個參數:cmd,操做命令
IPC_RMID ---刪除共享內存,此時第三個參數給NULL便可
第三個參數:buf結構體指針
返回值:成功返回0
失敗返回-1,errno被設置緩存
3.相關命令
ipcs -m
-------------------------
消息隊列
1.基本概念:
消息隊列就是在系統內核中保存的一個用來保存消息的隊列,這個隊列不是簡單的先進先出,還能夠控制消息更爲靈活。
2.基本通訊流程
1.獲取key值,使用ftok()函數
2.建立/獲取消息隊列,使用msgget()函數
3.發送/接收消息,使用msgsnd()函數和msgrcv()函數
4.若是再也不使用,刪除消息隊列,使用msgctl()函數
3.相關API函數
1.msgget()函數
int msgget(key_t key, int msgflg);
功能:主要用於建立/獲取消息隊列
第一個參數:key,ftok()的返回值
第二個參數:msgflg消息隊列的建立標誌
IPC_CREAT 建立
IPC_EXCL 與IPC_CREAT搭配使用,若消息隊列存在,則建立失敗
返回值:成功返回消息隊列的ID,失敗返回-1,errno被設置
注:
當建立新的消息隊列時,須要指定權限,如0644
2.msgsnd()函數
int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
功能:主要用於向指定的消息隊列發送指定的消息
第一個參數:msqid消息隊列的ID,msgget的返回值
第二個參數:msgp消息的首地址
消息的通常形式以下:
struct msgbuf {
long mtype; /* 消息類型, 必須大於0 */
char mtext[1]; /* 消息內容,可使用其它的數據類型 */
};
第三個參數:msgsz消息的大小,是指定的消息結構體中的內容的大小,不包括消息類型
第四個參數:發送的標誌,通常給0便可
返回值:成功返回0,失敗返回-1函數
3.msgrcv()函數
ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp,int msgflg);
功能:主要用於從消息隊列中接受消息存到指定的位置
第一個參數:msqid消息隊列的ID,msgget的返回值
第二個參數:消息的首地址(表示存到哪裏去)
第三個參數:消息的大小
第四個參數:消息的類型
0 ---表示接受消息隊列中的第一個消息
>0 ---表示接受消息隊列中的第一個類型爲msgtyp的消息
<0 ---表示接受消息隊列中的第一個小於等於msgtyp絕對值的消息,其中最小的類型優先讀取。
第五個參數:接受的標誌,通常給0便可
返回值:成功返回實際接受的數據大小,失敗返回-1,errno被設置
4.msgctl()函數
int msgctl(int msqid, int cmd, struct msqid_ds *buf);
第一個參數:msqid消息隊列的ID,msgget的返回值
第二個參數:cmd操做命令
IPC_RMID 刪除消息隊列,此時第三個參數給NULL便可。
第三個參數:結構指針
返回值:成功返回0,失敗返回-1,errno被設置指針
4.相關命令
ipcs -q
做業:
1.寫一個代碼嘗試求管道的大小
2.往共享內存中寫數據除了memcpy其它的方式
3.寫一個程序從消息隊列中收消息繼承
/*************************************************************************
> File Name: fifo.c
> Author: csgec
> Mail: longer.zhou@gmail.com
> Created Time: Wed 10 Aug 2016 11:32:55 AM CST
************************************************************************/隊列
#include<stdio.h>
#include<errno.h>
#include<stdlib.h>
#include<unistd.h>
#include<fcntl.h>
#include<string.h>
int main(int argc,char **argv)
{
int fd;
if(argc < 2)
{
printf("Usage: ./a.out path \n");
exit(-1);
}
if(mkfifo(argv[1],0666) < 0 && errno != EEXIST)
{
perror("mkfifo");
exit(-1);
}
fd = open(argv[1],O_WRONLY);
if(fd < 0)
{
perror("open");
exit(-1);
}
char *str = "hello fifo";
int r = write(fd,str,strlen(str));
printf("r = %d\n",r);
printf("建立FIFO成功\n");進程
}ip
/*************************************************************************
> File Name: fifo.c
> Author: csgec
> Mail: longer.zhou@gmail.com
> Created Time: Wed 10 Aug 2016 11:32:55 AM CST
************************************************************************/內存
#include<stdio.h>
#include<errno.h>
#include<stdlib.h>
#include<unistd.h>
#include<fcntl.h>
#include<string.h>
int main(int argc,char **argv)
{
int fd;
if(argc < 2)
{
printf("Usage: ./a.out path \n");
exit(-1);
}
if(mkfifo(argv[1],0666) < 0 && errno != EEXIST)
{
perror("mkfifo");
exit(-1);
}
fd = open(argv[1],O_RDONLY);
if(fd < 0)
{
perror("open");
exit(-1);
}
char buf[1024];
int r = read(fd,buf,sizeof(buf));
printf("r = %d\n",r);
printf("讀到的內容是:%s\n",buf);
}
/*************************************************************************
> File Name: pipe.c
> Author: csgec
> Mail: longer.zhou@gmail.com
> Created Time: Wed 10 Aug 2016 10:04:54 AM CST
************************************************************************/
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<string.h>
int main()
{
pid_t pid;
int pipefd[2] = {0};
int res = pipe(pipefd);
char buf[1024] = {0};
if(res == -1)
{
perror("pipe");
exit(-1);
}
printf("fd[0] = %d\n",pipefd[0]);
printf("fd[1] = %d\n",pipefd[1]);
pid = fork();
if(pid < 0)
{
perror("fork");
exit(-1);
}
else if(pid == 0)
{
close(pipefd[1]);
int r = read(pipefd[0],buf,sizeof(buf));
printf("我是子進程,我讀到%d個字節,內容是 :%s\n",r,buf);
close(pipefd[0]);
exit(0);
}
else
{
close(pipefd[0]);
char *str = "hello world";
int r = write(pipefd[1],str,strlen(str));
printf("我是父進程,成功寫入%d個字節,寫入的內容是%s\n",r,str);
close(pipefd[1]);
waitpid(pid,NULL,0);
}
}
#include<stdio.h>
#include<errno.h>
#include<stdlib.h>
#include<unistd.h>
#include<fcntl.h>
#include<string.h>
int main(int argc,char **argv)
{
int fd;
if(argc < 2)
{
printf("Usage: ./a.out path \n");
exit(-1);
}
if(mkfifo(argv[1],0666) < 0 && errno != EEXIST)
{
perror("mkfifo");
exit(-1);
}
fd = open(argv[1],O_WRONLY);
if(fd < 0)
{
perror("open");
exit(-1);
}
char *str = "hello fifo";
int r = write(fd,str,strlen(str));
printf("r = %d\n",r);
printf("建立FIFO成功\n");
}