高级I/O模型(1)--socketpair

非阻塞IO,纪录锁,系统V流机制,I/O多路转接(select/poll),readv和writev函数以及存储 映射IO(mmap),这些统称为高级IO。

pipe用来创建管道,但是单个管道只能单向通信,⼀端用于读,⽽而另一端用于写。如果要实现进程双向通信,必须创建一对管道。

而socketpair则可以⽤用来创建双向通 信的管道,

头文件:
#include 
#include 
原型:
int socketpair(int d,int protocol, int type, int sv[2]);   

参数介绍:
socketpair()函数建立一对匿名的已经连接的套接字,其特性由协议族d、类型type、协议protocol决定,建立的两个套接字描述符会放在sv[0]和sv[1]中。1个参数d,表示协议族,只能为AF_LOCAL或者AF_UNIX;
第2个参数type,表示类型,只能为0。3个参数protocol,表示协议,可以是SOCK_STREAM或者SOCK_DGRAM。用SOCK_STREAM建立的套接字对是管道流,与一般的管道相区别的是,套接字对建立的通道是双向的,即每一端都可以进行读写。参数sv,用于保存建立的套接字对。

socketpair创建了一对无名的套接字描述符(只能在AF_UNIX域中使用),描述符存储于一个二元数组,. fd[2] .这对套接字可以进行双工通信,每一个描述符既可以读也可以写。这个在同一个进程中也可以进行通信,向fd[0]中写入,就可以从fd[1]中读取(只能从s[1]中读取),也可以在fd[1]中写入,然后从fd[0]中读取;但是,若没有在0端写入,而从1端读取,则1端的读取操作会阻塞,即使在1端写入,也不能从1读取,仍然阻塞;反之亦然……

#include  
#include  
#include  
#include  
#include  
#include  
#include  
#include  


int main()
{ 
    int fd[2];  
    int w,r; 
    char * string = "hello world\n";    
    char * buf = (char*)calloc(1 , 100); 
    pid_t pid = fork(); 

    if( socketpair(AF_LOCAL, SOCK_STREAM, 0, fd) == -1 )
    {   
        perror("socketpair:");  
        return 1;
    }
    if( pid  > 0 )
    { 
        //father
        printf("I am your father!\n"); 
        close(fd[1]); 
        if( ( w = write(fd[0] , string , strlen(string) ) ) < 0 )
        { 
            perror("write\n"); 
            return 2;
        } 
    }else if(pid == 0)
    { 
        //child
        printf("Fork child process successed\n"); 
        printf("I am a child!\n"); 
        close(fd[0]); 
    }
    else
    { 
        perror("fork\n");                                       
        return 3;
    }
    if( (r = read(fd[1], buf , 100)) < 0)
    { 
        perror("read!\n"); 
        return 4;
    } 
    printf("Pid %d read string in same process : %s \n",getpid(),buf); 
    printf("Test successed , %d\n",getpid());
    return 0;
}

运行结果:
高级I/O模型(1)--socketpair_第1张图片

若fork子进程,然后在父进程关闭一个描述符fd[1] ,在子进程中再关闭另一个 fd[0] ,则可以实现父子进程之间的双工通信,两端都可读可写;当然,仍然遵守和在同一个进程之间工作的原则,一端写,在另一端读取;
具体实现如下:

#include 
#include 
#include 
#include 
#include 
#include 
#include 

int main()
{ 
    int fd[2];
    int ret = socketpair(PF_LOCAL, SOCK_STREAM, 0, fd);
    if(ret < 0)
    { 
        perror("socketpair:");
        return 1;
    }
    pid_t id = fork();
    if(id == 0)
    { 
        //child
        char* buf = "hello, I am a child\n";
        close(fd[0]);
        while(1)
        { 
            write(fd[1], buf, sizeof(buf));
            read(fd[1], buf,sizeof(buf));
            sleep(1);
            printf("child : %s\n", buf);
        }
    }
    else if(id > 0)
    { 
        //father
        char* buf = "I am your father\n";
        close(fd[1]);
        while(1)
        { 
            read(fd[0], buf, sizeof(buf));
            printf("father : %s\n", buf);
            write(fd[0], buf, sizeof(buf));
        }
    }
    else
    { 
        perror("fork");
        return 2;
    }
    return 0;
}

运行结果:
高级I/O模型(1)--socketpair_第2张图片

以上代码中在父子进程之间各关闭了一个描述符,则在父进程写可从子进程读取,反之若子进程写,父进程同样可以读取;

高级I/O模型(1)--socketpair_第3张图片

在父子进程中都不close(fd[1]),也就是保持两个读端,则父进程能够读到string串,但子进程读取空串,或者子进程先读了数据,父进程阻塞于read操作!

高级I/O模型(1)--socketpair_第4张图片

之所以子进程能读取父进程的string,是因为fork时,子进程继承了父进程的文件描述符的,同时也就得到了一个和父进程指向相同文件表项的指针;若父子进程均不关闭读端,因为指向相同的文件表项,这两个进程就有了竞争关系,争相读取这个字符串.父进程read后将数据转到其应用缓冲区,而子进程就得不到了,只有一份数据拷贝(若将父进程阻塞一段时间,则收到数据的就是子进程了,已经得到验证,让父进程sleep(3),子进程获得string,而父进程获取不到而是阻塞)

高级I/O模型(1)--socketpair_第5张图片

你可能感兴趣的:(linux)