`
bcyy
  • 浏览: 1826668 次
文章分类
社区版块
存档分类
最新评论

socketpair理解

 
阅读更多
今天跟人谈到socketpair的问题,晚上回来写了个程序验证下自己的猜测!

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

验证所用代码:

  1. #include<stdio.h>
  2. #include<string.h>
  3. #include<unistd.h>
  4. #include<sys/types.h>
  5. #include<error.h>
  6. #include<errno.h>
  7. #include<sys/socket.h>
  8. #include<stdlib.h>
  9. #defineBUF_SIZE30
  10. intmain(){
  11. ints[2];
  12. intw,r;
  13. char*string="Thisisateststring";
  14. char*buf=(char*)calloc(1,BUF_SIZE);
  15. if(socketpair(AF_UNIX,SOCK_STREAM,0,s)==-1){
  16. printf("createunnamedsocketpairfailed:%s\n",strerror(errno));
  17. exit(-1);
  18. }
  19. /*******testinasingleprocess********/
  20. if((w=write(s[0],string,strlen(string)))==-1){
  21. printf("Writesocketerror:%s\n",strerror(errno));
  22. exit(-1);
  23. }
  24. /*****read*******/
  25. if((r=read(s[1],buf,BUF_SIZE))==-1){
  26. printf("Readfromsocketerror:%s\n",strerror(errno));
  27. exit(-1);
  28. }
  29. printf("Readstringinsameprocess:%s\n",buf);
  30. if((r=read(s[0],buf,BUF_SIZE))==-1){
  31. printf("Readfromsockets0error:%s\n",strerror(errno));
  32. exit(-1);
  33. }
  34. printf("Readfroms0:%s\n",buf);
  35. printf("Testsuccessed\n");
  36. exit(0);
  37. }

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

这和pipe有一定的区别,pipe是单工通信,一端要么是读端要么是写端,而socketpair实现了双工套接字,也就没有所谓的读端和写端的区分

验证代码:

  1. #include<stdio.h>
  2. #include<string.h>
  3. #include<unistd.h>
  4. #include<sys/types.h>
  5. #include<error.h>
  6. #include<errno.h>
  7. #include<sys/socket.h>
  8. #include<stdlib.h>
  9. #defineBUF_SIZE30
  10. intmain(){
  11. ints[2];
  12. intw,r;
  13. char*string="Thisisateststring";
  14. char*buf=(char*)calloc(1,BUF_SIZE);
  15. pid_tpid;
  16. if(socketpair(AF_UNIX,SOCK_STREAM,0,s)==-1){
  17. printf("createunnamedsocketpairfailed:%s\n",strerror(errno));
  18. exit(-1);
  19. }
  20. /***********Test:forkbutdon'tcloseanyfdinneitherparentnorchildprocess***********/
  21. if((pid=fork())>0){
  22. printf("Parentprocess'spidis%d\n",getpid());
  23. close(s[1]);
  24. if((w=write(s[0],string,strlen(string)))==-1){
  25. printf("Writesocketerror:%s\n",strerror(errno));
  26. exit(-1);
  27. }
  28. }elseif(pid==0){
  29. printf("Forkchildprocesssuccessed\n");
  30. printf("Childprocess'spidis:%d\n",getpid());
  31. close(s[0]);
  32. }else{
  33. printf("Forkfailed:%s\n",strerror(errno));
  34. exit(-1);
  35. }
  36. /*****read***Inparentandchild****/
  37. if((r=read(s[1],buf,BUF_SIZE))==-1){
  38. printf("Pid%dreadfromsocketerror:%s\n",getpid(),strerror(errno));
  39. exit(-1);
  40. }
  41. printf("Pid%dreadstringinsameprocess:%s\n",getpid(),buf);
  42. printf("Testsuccessed,%d\n",getpid());
  43. exit(0);
  44. }

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

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

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

有网友"笨笨"回复:

“若将父进程阻塞一段时间,则收到数据的就是子进程了,已经得到验证,让父进程sleep(3),子进程获得string,而父进程获取不到”

我验证的情况是,父进程一直阻塞在read上。我想不明白,为什么这时候父进程不能读取数据呢。
而上一种情况,父进程先读取数据,子进程仍然可以读取数据(数据为空),但子进程不会阻塞在read上。

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

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

2.对于网友提到问题的这个测试,我最初的目的是想说明如果通过产生子进程的方式,对一个写端同时有多个读端,这这些读端之间相互竞争.我们可以用个更有说服力的测试方法来看出这个问题.原来的测试是让一个进程sleep然后另一个进程读完所有字符,可以看到之后醒来的进程就读不到任何字符了.更好的方法是先有一个进程读取一部分的字符,然后第二个进程被唤醒,会发现这第二个进程还能读到一些字符,而这些字符是第一个进程读完剩下的.

3.第一条中的遗留问题,为什么这两种情况有不同的表现.

  原因是:如果子进程先sleep,父进程读取完数据之后,父进程退出,此时写端s[0]的引用计数变为0(之前子进程已主动close了一次),被系统释放,根据read的语义,当子进程被唤醒后会读取到EOF;但是当我们先让父进程sleep的时候,子进程读取完后退出,由于写端在父进程,没有被释放,所以父进程此时阻塞在读操作上.

  用另外一个测试来证明,我们在子进程中不主动执行close[0],也就是有两个写端,然后其他不变,子进程先sleep,父进程先读取到数据然后退出,但此时更刚刚有个区别,父进程退出的时候s[0]这个写端的描述符并不会减到0,因为子进程中还持有一个引用,所以写端健在,子进程被唤醒之后不会读到EOF返回,而是阻塞在读操作上

最后,有关socketpair在内核中实现的一点点描述:

socketpair会创建两个描述符,但改描述符不属于任何的实际文件系统,而是网络文件系统,虚拟的.同时内核会将这两个描述符彼此设为自己的peer即对端(这里即解决了如何标识读写端,可以想象,两个描述符互为读写缓冲区,即解决了这个问题).然后应用相应socket家族里的read/write函数执行读写操作.

有了这个基础,即可明白为什么试用fork产生的两个子进程都不关闭读端的时候会竞争,如上所述,他们共享相同的文件表项,有相同的inode和偏移量,两个进程的操作当然是相互影响的.

本文出自 “流离and逍遥” 博客,请务必保留此出处http://liulixiaoyao.blog.51cto.com/1361095/533469

分享到:
评论

相关推荐

    TCP/IP详解 卷3:TCP事务协议、HTTP、NNTP和UNIX域协议

    17.12 socketpair系统调用 17.13 pipe系统调用 17.14 PRU_ACCEPT请求 17.15 PRU_DISCONNECT请求和unp_disconnect函数 17.16 PRU_SHUTDOWN请求和unp_shutdown函数 17.17 PRU_ABORT请求和unp_drop函数 17.18 其他各种...

    传智播客扫地僧视频讲义源码

    02_socketclient_api模型的抽象_初学者应知的标准_传智扫地僧 03_本套视频总体课程简介 04_就业班课程总体简介_课堂答疑 05_初学者建立信心 06_学员学习标准_排序及问题抛出 07_数组做函数参数退化问题剖析_传智扫地...

    java源码包---java 源码 大量 实例

     Java生成密钥、保存密钥的实例源码,通过本源码可以了解到Java如何产生单钥加密的密钥(myKey)、产生双钥的密钥对(keyPair)、如何保存公钥的字节数组、保存私钥到文件privateKey.dat、如何用Java对象序列化保存私钥...

    java源码包2

     Java生成密钥、保存密钥的实例源码,通过本源码可以了解到Java如何产生单钥加密的密钥(myKey)、产生双钥的密钥对(keyPair)、如何保存公钥的字节数组、保存私钥到文件privateKey.dat、如何用Java对象序列化保存私钥...

    java源码包3

     Java生成密钥、保存密钥的实例源码,通过本源码可以了解到Java如何产生单钥加密的密钥(myKey)、产生双钥的密钥对(keyPair)、如何保存公钥的字节数组、保存私钥到文件privateKey.dat、如何用Java对象序列化保存私钥...

    java源码包4

     Java生成密钥、保存密钥的实例源码,通过本源码可以了解到Java如何产生单钥加密的密钥(myKey)、产生双钥的密钥对(keyPair)、如何保存公钥的字节数组、保存私钥到文件privateKey.dat、如何用Java对象序列化保存私钥...

    成百上千个Java 源码DEMO 4(1-4是独立压缩包)

    Java生成密钥的实例 1个目标文件 摘要:Java源码,算法相关,密钥 Java生成密钥、保存密钥的实例源码,通过本源码可以了解到Java如何产生单钥加密的密钥(myKey)、产生双钥的密钥对(keyPair)、如何保存公钥的字节数组、...

    成百上千个Java 源码DEMO 3(1-4是独立压缩包)

    Java生成密钥的实例 1个目标文件 摘要:Java源码,算法相关,密钥 Java生成密钥、保存密钥的实例源码,通过本源码可以了解到Java如何产生单钥加密的密钥(myKey)、产生双钥的密钥对(keyPair)、如何保存公钥的字节数组、...

    JAVA上百实例源码以及开源项目

     Java生成密钥、保存密钥的实例源码,通过本源码可以了解到Java如何产生单钥加密的密钥(myKey)、产生双钥的密钥对(keyPair)、如何保存公钥的字节数组、保存私钥到文件privateKey.dat、如何用Java对象序列化保存私钥...

    JAVA上百实例源码以及开源项目源代码

     Java生成密钥、保存密钥的实例源码,通过本源码可以了解到Java如何产生单钥加密的密钥(myKey)、产生双钥的密钥对(keyPair)、如何保存公钥的字节数组、保存私钥到文件privateKey.dat、如何用Java对象序列化保存私钥...

    TCP_IP详解卷1

    对tcpdump输出的研究可以帮助理解不同协议如何工作。 本书适合作为计算机专业学生学习网络的教材和教师参考书。也适用于研究网络的技术人员。 目 录 译者序 前言 第1章 概述 1 1.1 引言 1 1.2 分层 1 1.3 TCP/IP的...

    TCPIP详解卷[1].part04

    对tcpdump输出的研究可以帮助理解不同协议如何工作。 本书适合作为计算机专业学生学习网络的教材和教师参考书。也适用于研究网络的技术人员。 目 录 译者序 前言 第1章 概述 1 1.1 引言 1 1.2 分层 1 1.3 TCP/IP的...

    TCPIP详解卷[1].part09

    对tcpdump输出的研究可以帮助理解不同协议如何工作。 本书适合作为计算机专业学生学习网络的教材和教师参考书。也适用于研究网络的技术人员。 目 录 译者序 前言 第1章 概述 1 1.1 引言 1 1.2 分层 1 1.3 TCP/IP的...

    TCPIP详解卷[1].part05

    对tcpdump输出的研究可以帮助理解不同协议如何工作。 本书适合作为计算机专业学生学习网络的教材和教师参考书。也适用于研究网络的技术人员。 目 录 译者序 前言 第1章 概述 1 1.1 引言 1 1.2 分层 1 1.3 TCP/IP的...

    TCPIP详解卷[1].part03

    对tcpdump输出的研究可以帮助理解不同协议如何工作。 本书适合作为计算机专业学生学习网络的教材和教师参考书。也适用于研究网络的技术人员。 目 录 译者序 前言 第1章 概述 1 1.1 引言 1 1.2 分层 1 1.3 TCP/IP的...

    TCPIP详解卷[1].part06

    对tcpdump输出的研究可以帮助理解不同协议如何工作。 本书适合作为计算机专业学生学习网络的教材和教师参考书。也适用于研究网络的技术人员。 目 录 译者序 前言 第1章 概述 1 1.1 引言 1 1.2 分层 1 1.3 TCP/IP的...

    TCP/IP详解part_2

    对tcpdump输出的研究可以帮助理解不同协议如何工作。 本书适合作为计算机专业学生学习网络的教材和教师参考书。也适用于研究网络的技术人员。 目 录 译者序 前言 第1章 概述 1 1.1 引言 1 1.2 分层 1 1.3 TCP/IP的...

    TCPIP详解卷[1].part08

    对tcpdump输出的研究可以帮助理解不同协议如何工作。 本书适合作为计算机专业学生学习网络的教材和教师参考书。也适用于研究网络的技术人员。 目 录 译者序 前言 第1章 概述 1 1.1 引言 1 1.2 分层 1 1.3 TCP/IP的...

    TCPIP详解卷[1].part11

    对tcpdump输出的研究可以帮助理解不同协议如何工作。 本书适合作为计算机专业学生学习网络的教材和教师参考书。也适用于研究网络的技术人员。 目 录 译者序 前言 第1章 概述 1 1.1 引言 1 1.2 分层 1 1.3 TCP/IP的...

Global site tag (gtag.js) - Google Analytics