首页 > 其他分享 >对sockpair加fork的理解

对sockpair加fork的理解

时间:2023-05-19 20:25:52浏览次数:28  
标签:fork 读取 read 理解 printf 进程 include sockpair string

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

#include <stdio.h> 

#include <string.h> 

#include <unistd.h> 

#include <sys/types.h> 

#include <error.h> 

#include <errno.h> 

#include <sys/socket.h> 

#include <stdlib.h> 

#define BUF_SIZE 30 

int main(){ 

        int s[2]; 

        int w,r; 

        char * string = "This is a test string"; 

        char * buf = (char*)calloc(1 , BUF_SIZE); 

        if( socketpair(AF_UNIX,SOCK_STREAM,0,s) == -1 ){ 

                printf("create unnamed socket pair failed:%s\n",strerror(errno) ); 

                exit(-1); 

        } 

        /*******test in a single process ********/ 

        if( ( w = write(s[0] , string , strlen(string) ) ) == -1 ){ 

                printf("Write socket error:%s\n",strerror(errno)); 

                exit(-1); 

        } 

        /*****read*******/ 

        if( (r = read(s[1], buf , BUF_SIZE )) == -1){ 

                printf("Read from socket error:%s\n",strerror(errno) ); 

                exit(-1); 

        } 

        printf("Read string in same process : %s \n",buf); 

        if( (r = read(s[0], buf , BUF_SIZE )) == -1){ 

             printf("Read from socket s0 error:%s\n",strerror(errno) ); 

                 exit(-1); 

        } 

        printf("Read from s0 :%s\n",buf); 

        printf("Test successed\n"); 

        exit(0); 

}

若fork子进程,然后在服进程关闭一个描述符eg. s[1] ,在子进程中再关闭另一个 eg. s[0] ,则可以实现父子进程之间的双工通信,两端都可读可写;当然,仍然遵守和在同一个进程之间工作的原则,一端写,在另一端读取;这和pipe有一定的区别,pipe是单工通信,一端要么是读端要么是写端,而socketpair实现了双工套接字,也就没有所谓的读端和写端的区分

#include <stdio.h> 

#include <string.h> 

#include <unistd.h> 

#include <sys/types.h> 

#include <error.h> 

#include <errno.h> 

#include <sys/socket.h> 

#include <stdlib.h> 

#define BUF_SIZE 30 

int main(){ 

        int s[2]; 

        int w,r; 

        char * string = "This is a test string"; 

        char * buf = (char*)calloc(1 , BUF_SIZE); 

        pid_t pid; 

        if( socketpair(AF_UNIX,SOCK_STREAM,0,s) == -1 ){ 

                printf("create unnamed socket pair failed:%s\n",strerror(errno) ); 

                exit(-1); 

        } 

        /***********Test : fork but don't close any fd in neither parent nor child process***********/ 

        if( ( pid = fork() ) > 0 ){ 

                printf("Parent process's pid is %d\n",getpid()); 

                close(s[1]); 

                if( ( w = write(s[0] , string , strlen(string) ) ) == -1 ){ 

                        printf("Write socket error:%s\n",strerror(errno)); 

                        exit(-1); 

                } 

        }else if(pid == 0){ 

                printf("Fork child process successed\n"); 

                printf("Child process's pid is :%d\n",getpid()); 

                close(s[0]); 

        }else{ 

                printf("Fork failed:%s\n",strerror(errno)); 

                exit(-1); 

        } 

        /*****read***In parent and child****/ 

        if( (r = read(s[1], buf , BUF_SIZE )) == -1){ 

                printf("Pid %d read from socket error:%s\n",getpid() , strerror(errno) ); 

                exit(-1); 

        } 

        printf("Pid %d read string in same process : %s \n",getpid(),buf); 

        printf("Test successed , %d\n",getpid()); 

        exit(0); 

}

以上代码中在父子进程之间各关闭了一个描述符,则在父进程写可从子进程读取,反之若子进程写,父进程同样可以读取;大家可以验证下.另外,我也测试了在父子进程中都不close(s[1]),也就是保持两个读端,则父进程能够读到string串,但子进程读取空串,或者子进程先读了数据,父进程阻塞于read操作!之所以子进程能读取父进程的string,是因为fork时,子进程继承了父进程的文件描述符的,同时也就得到了一个和父进程指向相同文件表项的指针;若父子进程均不关闭读端,因为指向相同的文件表项,这两个进程就有了竞争关系,争相读取这个字符串.父进程read后将数据转到其应用缓冲区,而子进程就得不到了,只有一份数据拷贝(若将父进程阻塞一段时间,则收到数据的就是子进程了,已经得到验证,让父进程sleep(3),子进程获得string,而父进程获取不到而是阻塞)

有网友"笨笨"回复:

若将父进程阻塞一段时间,则收到数据的就是子进程了,已经得到验证,让父进程sleep(3),子进程获得string,而父进程获取不到”我验证的情况是,父进程一直阻塞在read上。我想不明白,为什么这时候父进程不能读取数据呢。

而上一种情况,父进程先读取数据,子进程仍然可以读取数据(数据为空),但子进程不会阻塞在read上。

关于这个问题,解释如下:

1.该网友说的情况的确存在,如果先让子进程sleep,此时父进程获得数据,子进程被唤醒之后读到EOF返回;若是让父进程sleep先,子进程先获取数据,之后父进程被唤醒却是一直阻塞不能返回.按理来说这两种情况应该没差别,这个区别下文描述.

2.对于网友提到问题的这个测试,我最初的目的是想说明如果通过产生子进程的方式,对一个写端同时有多个读端,这这些读端之间相互竞争.我们可以用个更有说服力的测试方法来看出这个问题.原来的测试是让一个进程sleep然后另一个进程读完所有字符,可以看到之后醒来的进程就读不到任何字符了.更好的方法是先有一个进程读取一部分的字符,然后第二个进程被唤醒,会发现这第二个进程还能读到一些字符,而这些字符是第一个进程读完剩下的.\n\n3.第一条中的遗留问题,为什么这两种情况有不同的表现.\n\n原因是:如果子进程先sleep,父进程读取完数据之后,父进程退出,此时写端s[0]的引用计数变为0(之前子进程已主动close了一次),被系统释放,根据read的语义,当子进程被唤醒后会读取到EOF;但是当我们先让父进程sleep的时候,子进程读取完后退出,由于写端在父进程,没有被释放,所以父进程此时阻塞在读操作上.\n\n用另外一个测试来证明,我们在子进程中不主动执行close[0],也就是有两个写端,然后其他不变,子进程先sleep,父进程先读取到数据然后退出,但此时更刚刚有个区别,父进程退出的时候s[0]这个写端的描述符并不会减到0,因为子进程中还持有一个引用,所以写端健在,子进程被唤醒之后不会读到EOF返回,而是阻塞在读操作上\n\n最后,有关socketpair在内核中实现的一点点描述:\n\nsocketpair会创建两个描述符,但改描述符不属于任何的实际文件系统,而是网络文件系统,虚拟的.同时内核会将这两个描述符彼此设为自己的peer即对端(这里即解决了如何标识读写端,可以想象,两个描述符互为读写缓冲区,即解决了这个问题).然后应用相应socket家族里的read/write函数执行读写操作.\n\n有了这个基础,即可明白为什么试用fork产生的两个子进程都不关闭读端的时候会竞争,如上所述,他们共享相同的文件表项,有相同的inode和偏移量,两个进程的操作当然是相互影响的.

 

 ☞文件经转载

标签:fork,读取,read,理解,printf,进程,include,sockpair,string
From: https://www.cnblogs.com/Zhaolongtao/p/17416185.html

相关文章

  • Nacos 核心原理解读+高性能微服务系统实战
    Nacos核心原理解读+高性能微服务系统实战download:3w51xuebccom《模拟人生4》(TheSims4)是一款由Maxis和TheSimsStudio开发,由ElectronicArts发行的模拟人生游戏。它被广泛认为是模拟人生系列中最好玩的一部分。本文将向您介绍TS4的入门知识。TS4的基本概念在TS4中,你可以创建......
  • 深入理解JavaScript的变量作用域
    一篇整理的不错的文章。  深入理解JavaScript的变量作用域1、JavaScript的作用域链2、函数体内部,局部变量的优先级比同名的全局变量高。3、JavaScript没有块级作用域。4、函数中声明的变量在整个函数中都有定义。5、未使用var关键字定义的变量都是全局变量。6、全局变量都是win......
  • 分布式事务理解
    分布式系统:是一个硬件或软件组件分布在不同的网络计算机上,彼此之间仅仅通过消息传递进行通信和协调的系统。分布式事务:是指事务的参与者、支持事务的服务器、资源服务器以及事务管理器分别位于不同的分布式系统的不同节点之上。根据分布式系统CAP定理和base理论可知,分布式系统只......
  • 深入理解之JavaScript之call, apply, bind方法
    在JavaScript中,call、apply和bind是Function对象自带的三个方法,这三个方法的主要作用是改变函数执行时的上下文,再具体一点就是改变函数运行时的this指向。Function.prototype.call()call()方法调用一个函数,其具有一个指定的this值和多个参数(参数的列表)。fun.call(thisArg,a......
  • 彻底理解synchronized(转载)
    synchronized简介在学习知识前,我们先来看一个现象:publicclassSynchronizedDemoimplementsRunnable{privatestaticintcount=0;publicstaticvoidmain(String[]args){for(inti=0;i<10;i++){Threadthread=newThread(n......
  • 关于int main(int argc, const char * argv[])的理解
    命令行参数前面一个是int值,理论上后面一个char型的指针数组,每个字符型的指针都存贮一个字符串(很像shell命令,$#输出所有命令行参数个数,不包括命令本身,$*,输出所有命令行参数)argc命令行执行时输入字符串的个数,argv输出字符串的内容(输入都默认字符串类型)所以下面这个程序输入......
  • 产废单位EPM+SAP产废企业数字化管理解决方案——环保数字化转型利器
    哲讯产废单位EPM+SAP产废企业数字化解决方案随着环保部门对重点产废单位监管的日益加强,产废企业必须要做好内部危废转移管理。基于成熟的危废管理系统衍生产品“产废管理系统”,可以帮助产废企业规范化管理内部危废的转运与处置,轻松应对环保监管。系统设计遵循开放性原则,各系统采......
  • GRPC与 ProtoBuf 的理解与总结
    转载请注明出处:1.GRPC官网:https://www.grpc.io/gRPC官方文档中文版:http://doc.oschina.net/grpcRPC框架的目标就是让远程服务调用更加简单、透明,其负责屏蔽底层的传输方式(TCP/UDP)、序列化方式(XML/Json)和通信细节。服务调用者可以像调用本地接口一样调用远程的......
  • 深入理解JavaScript之作用域链与闭包
    作用域作用域是指程序源代码中定义变量的区域。实际上描述的就是查找变量的范围,作用域必须有的两个功能就是存储变量以及查找变量,作用域就是发挥这两个作用以及更多作用的规则。作用域规定了如何查找变量,也就是确定当前执行代码对变量的访问权限。词法作用域和动态作用域词法作用域......
  • 全面理解Java内存模型
    Java内存模型即JavaMemoryModel,简称JMM。JMM定义了Java虚拟机(JVM)在计算机内存(RAM)中的工作方式。JVM是整个计算机虚拟模型,所以JMM是隶属于JVM的。如果我们要想深入了解Java并发编程,就要先理解好Java内存模型。Java内存模型定义了多线程之间共享变量的可见性以及如何在需要的......