pipe管道


回顧:
進程間通信方式:
信號,管道
消息隊列,共享內存,信號量
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");

}

 

/*************************************************************************
> 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");

}

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM