1. 程式人生 > >Linux學習之程序通訊(一)

Linux學習之程序通訊(一)

言之者無罪,聞之者足以戒。 ——《詩序》

ctrl+alt+t     開啟終端

一、程序間通訊和執行緒間通訊:

程序通訊:在使用者空間實現程序通訊是不可能的,通過Linux核心通訊

執行緒通訊:可以在使用者空間就可以實現,可以通過全域性變數通訊

二、通訊方式:

管道通訊:無名管道、有名管道(檔案系統中有名)

訊號通訊:訊號(通知)通訊包括:訊號的傳送、訊號的接收和訊號的處理

IPC(Inyer-Process Communication) 通訊:共享記憶體、訊息佇列和訊號燈。

以上是單機模式下的程序通訊(只有一個Linux核心)

Socket通訊:存在於一個網路中兩個程序之間的通訊(兩個Linux核心)。

三、程序通訊學習思路:

每一種通訊方式都是基於檔案IO的思想

open:功能:建立或開啟程序通訊物件。函式的形式不一樣,有的是多個函式完成

write:功能:向程序通訊物件中寫入內容。函式形式可能不一樣

read:功能:從程序通訊物件中讀取內容。函式形式可能不一樣

close:功能:關閉或刪除程序通訊物件。形式可能不一樣

四、無名管道:

通訊原理:

管道檔案是一個特殊的檔案,是由佇列來實現的。(佇列一端入隊,一端出對)

在檔案IO中建立一個檔案或開啟一個檔案是由open函式來實現的,但open函式不能建立管道檔案

只能用pipe函式來建立管道。

函式形式:int pipe(int fd[2])

功能:建立管道,為系統呼叫:unistd.h

引數:就是得到的檔案描述符。可見有兩個檔案描述符:fd[0]和fd[1],管道有一個讀端fd[0]用來讀和一個寫端fd[1]用來寫這個規矩不能改變

返回值:成功是0,出錯是-1

注意:

管道中的東西,讀完了就刪除了,就像佇列中的出對一樣

如果管道中沒有東西可讀,則會出現讀阻塞

如果管道已經被寫滿,還要寫東西就會出現寫阻塞

下面寫一個程式實現:

(1)建立一個管道

(2)向管道中寫資料

(3)讀取管道中的資料

(4)驗證管道的讀阻塞

程式如下:

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main()
{
        int fd[2];
        int ret;
        char writebuf[]="hello linux";
        char readbuf[128]={0};
        ret=pipe(fd);
        if(ret < 0)
        {
                printf("creat pipe failure\n");
                return -1;
        }
        printf("creat pipe sucess fd[0]=%d,fd[1]=%d\n",fd[0],fd[1]);

        write(fd[1],writebuf,sizeof(writebuf));

        read(fd[0],readbuf,128);
        printf("readbuf=%s\n",readbuf);
        //validate read block
        memset(readbuf,0,128);//clear readbuf

        read(fd[0],readbuf,128);
        printf("read again sucess\n");

        close(fd[0]);
        close(fd[1]);
        return 0;
}

命令:ps -axj 可以檢視程序的狀態

下面寫一個程式實現:

(1)建立一個管道

(2)向管道中寫資料

(3)驗證寫阻塞

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main()
{
        int fd[2];
        int ret;
        int i=0;
        char writebuf[]="hello linux";
        char readbuf[128]={0};
        ret=pipe(fd);
        if(ret < 0)
        {
                printf("creat pipe failure\n");
                return -1;
        }
        printf("creat pipe sucess fd[0]=%d,fd[1]=%d\n",fd[0],fd[1]);
        while(i < 5457)
        {
        write(fd[1],writebuf,sizeof(writebuf));
        i++;
        }
        printf("write is sucess\n");

        close(fd[0]);
        close(fd[1]);
        return 0;
}

通過程式的驗證:我們發現了阻塞空間的大小,當i<=5456時不阻塞,當i>=5457時阻塞

無名管道實現程序通訊:

#include <unistd.h>
#include <stdio.h>
#include <sys/types.h>
#include <stdlib.h>
int main()
{
        pid_t pid;
        int fd[2];
        int ret;
        char process_inter=0;
        ret=pipe(fd);
        if(ret < 0)
        {
                printf("creat pipe failure\n");
                return -1;
        }
        printf("creat pipe sucess\n");
        pid = fork();
        if(pid==0)
        {
                int i=0;
                read(fd[0],&process_inter,1);
                while(process_inter==0);
                for(i=0;i<5;i++)
                {
                        printf("this is child process i=%d\n",i);
                        usleep(100);
                }
        }
        if(pid>0)
        {
                int i=0;
                for(i=0;i<5;i++)
                {
                        printf("this is parent process i=%d\n",i);
                        usleep(100);
                }
                process_inter=1;
                sleep(5);
                write(fd[1],&process_inter,1);
        }
        while(1);
        return 0;
}

無名管道的缺點:只能實現父子程序(有親緣關係程序)之間的通訊。

五、有名管道:

正是由於無名管道的這一缺點,我們對無名管道進行改進:有名管道

所謂的有名,即檔案系統中存在這樣一個檔案節點,每一個檔案都有一個inode號,而且這是一個特殊的檔案型別:p管道型別

1、建立這個檔案節點,不可以通過open函式,open函式只能建立普通檔案,不能建立特殊檔案(管道-mkfifo、套接字-socket、字元裝置檔案-mknod、塊裝置檔案-mknod、符號連結檔案-ln -s、目錄檔案mkdir)

2、管道檔案只有inode號,和套接字、字元裝置檔案、塊裝置檔案一樣都不佔用磁碟空間。普通檔案和符號檔案以及目錄檔案,不僅有inode 號,而且還佔用磁碟空間。

3、mkfifo  用來建立管道檔案的節點,沒有在核心中建立管道,只是通過open函式開啟這個檔案時才會在核心空間建立管道

4、mkfifo

函式形式:int mkfifo(const *filename , mode_t mode)

功能:建立管道檔案

引數:管道檔名,許可權,建立的檔案許可權仍然和umask有關係

返回值:建立成功返回0,建立失敗返回-1

下面看一下mkfifo的用法:

include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
int main()
{
        int ret;
        ret=mkfifo("./myfifo",0777);
        if(ret < 0)
        {
                printf("creat myfifo failure\n");
                return -1;
        }
        printf("creat myfifo sucess\n");
        return 0;
}

下面我們就通過有名管道實現無親緣關係程序間的通訊:

(1)第一步:建立有名管道:

include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
int main()
{
        int ret;
        ret=mkfifo("./myfifo",0777);
        if(ret < 0)
        {
                printf("creat myfifo failure\n");
                return -1;
        }
        printf("creat myfifo sucess\n");
        return 0;
}

(2)第二步:建立第一個程序:

include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <stdlib.h>
#include <fcntl.h>
int main()
{
        int fd;
        int i;
        char process_inter1=0;
        fd=open("./myfifo",O_WRONLY);
        if(fd < 0)
        {
                printf("open myfifo failure\n");
                return -1;
        }
        printf("open myfifo sucess\n");
        for(i=0;i<5;i++)
        {
                printf("this is first process i=%d\n",i);
                usleep(100);
        }
        process_inter1=1;
        sleep(5);
        write(fd,&process_inter1,1);
        while(1);
        return 0;
}

(3)第三步:建立第二個程序:  

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <stdlib.h>
#include <fcntl.h>
int main()
{
        int fd;
        int i;
        char process_inter2=0;
        fd=open("./myfifo",O_RDONLY);
        if(fd < 0)
        {
                printf("open myfifo failure\n");
                return -1;
        }
        printf("open myfifo sucess\n");
        read(fd,&process_inter2,1);
        while(process_inter2==0);
        for(i=0;i<5;i++)
        {
                printf("this is second process i=%d\n",i);
                usleep(100);
        }
        while(1);
        return 0;
}

(4)第四步:首先編譯執行第一個程序,緊接著編譯執行第二個程序;當我們執行第一個程序之後不會看到列印資訊,那是因為只有讀寫兩端都被啟動,程序才會被啟動,當我們執行第二個程序之後就可以看到第一個程序的列印資訊,隨後會看到第二個程序的列印資訊(用兩個終端執行兩個程序)。