進程間通信-信號-pipe-fifo
Linux進程間通信
進程是程序運行資源分配的最小單位。每個進程各自有不同的用戶地址空間,任何一個進程的全局變量在另一個進程中都看不到,所以進程之間要交換數據必須通過內核,在內核中開辟一塊緩沖區,進程1把數據從用戶空間拷到內核緩沖區,進程2再從內核緩沖區把數據讀走,內核提供的這種機制稱為進程間通信(IPC,Inter-Process Communication)。
匿名管道
管道的創建
管道是一種最基本的進程間通信機制。管道由pipe函數來創建:

來看看通過管道進行通信的步驟:
》父進程創建管道,得到兩個文件描述符指向管道的兩端

》利用fork函數創建出子進程,則子進程也得到兩個文件描述符指向同一管道

》父進程關閉讀端(pipe[0]),子進程關閉寫端pipe[1],則此時父進程可以往管道中進行寫操作,子進程可以從管道中讀,從而實現了通過管道的進程間通信。
示例代碼
#include<stdio.h>
#include<unistd.h>
#include<string.h>
int main()
{
int _pipe[2];
int ret=pipe(_pipe);
if(ret<0)
{
perror("pipe\n");
}
pid_t id=fork();
if(id<0)
{
perror("fork\n");
}
else if(id==0) // child
{
close(_pipe[0]);
int i=0;
char *mesg=NULL;
while(i<100)
{
mesg="I am child";
write(_pipe[1],mesg,strlen(mesg)+1);
sleep(1);
++i;
}
}
else //father
{
close(_pipe[1]);
int j=0;
char _mesg[100];
while(j<100)
{
memset(_mesg,'\0',sizeof(_mesg ));
read(_pipe[0],_mesg,sizeof(_mesg));
printf("%s\n",_mesg);
j++;
}
}
return 0;
}
pipe的特點:
-
只能單向通信
-
只能血緣關系的進程進行通信
-
依賴于文件系統
-
生命周期隨進程
-
面向字節流的服務
-
管道內部提供了同步機制
說明:因為管道通信是單向的,在上面的例子中我們是通過子進程寫父進程來讀,如果想要同時父進程寫而子進程來讀,就需要再打開另外的管道;
管道的讀寫端通過打開的文件描述符來傳遞,因此要通信的兩個進程必須從它們的公共祖先那里繼承管道的件描述符。 上面的例子是父進程把文件描述符傳給子進程之后父子進程之 間通信,也可以父進程fork兩次,把文件描述符傳給兩個子進程,然后兩個子進程之間通信, 總之 需要通過fork傳遞文件描述符使兩個進程都能訪問同一管道,它們才能通信。
命名管道FIFO
在管道中,只有具有血緣關系的進程才能進行通信,對于后來的命名管道,就解決了這個問題。FIFO不同于管道之處在于它提供一個路徑名與之關聯,以FIFO的文件形式存儲于文件系統中。命名管道是一個設備文件,因此,即使進程與創建FIFO的進程不存在親緣關系,只要可以訪問該路徑,就能夠通過FIFO相互通信。值得注意的是, FIFO(first input first output)總是按照先進先出的原則工作,第一個被寫入的數據將首先從管道中讀出。
命名管道的創建
創建命名管道的系統函數有兩個:mknod和mkfifo。兩個函數均定義在頭文件sys/stat.h,
函數原型如下:
#include <sys/types.h>
#include <sys/stat.h>
int mknod(const char *path,mode_t mod,dev_t dev);
int mkfifo(const char *path,mode_t mode);
函數mknod參數中path為創建的命名管道的全路徑名: mod為創建的命名管道的模指明其存取權限; dev為設備值,該值取決于文件創建的種類,它只在創建設備文件時才會用到。這兩個函數調用成功都返回0,失敗都返回-1。下面使用mknod函數創建了一個命名管道:
umask(0);
if (mknod("/tmp/fifo",S_IFIFO | 0666) == -1)
{
perror("mkfifo error");
exit(1);
}
函數mkfifo前兩個參數的含義和mknod相同。下面是使用mkfifo的示例代碼:
umask(0);
if (mkfifo("/tmp/fifo",S_IFIFO|0666) == -1)
{
perror("mkfifo error!");
exit(1);
}
-
"S_IFIFO|0666"指明創建一個命名管道且存取權限為0666,即創建者、與創建者同組的用戶、其他用戶對該命名管道的訪問權限都是可讀可寫( 這里要注意umask對生成的管道文件權限的影響)。
-
命名管道創建后就可以使用了,命名管道和管道的使用方法法基本是相同的。只是使用命名管道時,必須先調用open()將其打開。因為命名管道是一個存在于硬盤上的文件,而管道是存在于內存中的特殊文件。
-
需要注意的是,調用open()打開命名管道的進程可能會被阻塞。但如果同時用讀寫方式( O_RDWR)打開,則一定不會導致阻塞;如果以只讀方式( O_RDONLY)打開,則調用open()函數的進程將會被阻塞直到有寫方打開管道;同樣以寫方式( O_WRONLY)打開也會阻塞直到有讀方式打開管道。
運行示例
Server.c:
#include<stdio.h>
#include<unistd.h>
#include<sys/types.h>
#include<string.h>
#include<sys/stat.h>
#include<fcntl.h>
#define _PATH_NAME_ "/tmp/file.tmp"
#define _SIZE_ 100
int main()
{
int ret=mkfifo(_PATH_NAME_,S_IFIFO|0666);
if(ret==-1){
printf("make fifo error\n");
return 1;
}
char buf[_SIZE_];
memset(buf,'\0',sizeof(buf));
int fd=open(_PATH_NAME_,O_WRONLY);
while(1)
{
//scanf("%s",buf);
fgets(buf,sizeof(buf)-1,stdin);
int ret=write(fd,buf,strlen(buf)+1);
if(ret<0){
printf("write error");
break;
}
}
close(fd);
return 0;
}
Client.c:
#include<stdio.h>
#include<sys/stat.h>
#include<unistd.h>
#include<fcntl.h>
#include<sys/types.h>
#include<string.h>
#define _PATH_NAME "/tmp/file.tmp"
#define _SIZE_ 100
int main()
{
int fd=open(_PATH_NAME,O_RDONLY);
if(fd<0){
printf("open file error");
return 1;
}
char buf[_SIZE_];
memset(buf,'\0',sizeof(buf));
while(1)
{
int ret=read(fd,buf,sizeof(buf));
if(ret<0){
printf("read end or error\n");
break;
}
printf("%s",buf);
}
close(fd);
return 0;
}
posted on 2022-11-09 15:14 20201321周慧琳 閱讀(49) 評論(0) 收藏 舉報
浙公網安備 33010602011771號