域套接字sendto errno -11分析

这篇具有很好参考价值的文章主要介绍了域套接字sendto errno -11分析。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

sendto errno -11代码分析

errno -11在内核代码中代表EAGAIN(再试⼀次),域套接字sendto过程中 sendto->sock_sendmsg->unix_dgram_sendmsg,在unix_dgram_sendmsg中有两处会返回 EAGAIN:
第1处:sock_alloc_send_pskb
第2处:
other!=sk&&unlikely(unix_peer(other)!=sk&&unix_recvq_full_lockless(other))
unix_peer(sk)!=other||unix_dgram_peer_wake_me(sk,other)
当以上两个条件都满⾜时也会返回 EAGAIN。
另外需要注意的是unix_dgram_sendmsg中直接通过skb_queue_tail(&other->sk_receive_queue,skb)将数据放⼊了对端的接收队列中。
域套接字sendto errno -11分析

第1处
sock_alloc_send_pskb函数中当socket发送缓冲区满时( sk_wmem_alloc_get(sk)>=sk->sk_sndbuf)将返回 EAGAIN。
域套接字sendto errno -11分析
第2处

在 Linux 内核源代码中,unix_peer(other) != sk 表⽰另⼀个 Unix 域套接字( other )的对端套接字(peer socket)不等于当前套接字( sk )本⾝。
在 Unix 域套接字通信中,每个发起连接的进程(或线程)都必须创建两个⽂件描述符,⼀个⽤于客⼾端(client),称为客⼾端套接字(client socket),另⼀个⽤于服务器端
(server),称为服务器套接字(server socket)。这两个套接字通过 Unix 域⽂件系统中的某个路径名进⾏连接(bind)。
当对等⽅成功建⽴连接后,两个套接字中的⼀个将⾃动成为对⽅的对端套接字(peer socket)。这意味着两个对等⽅都有⼀个指向对⽅套接字的结构体,也就是所谓的“peer
socket”。
因此,unix_peer(other) != sk 表⽰当前套接字( sk )不是另⼀个 Unix 域套接字( other )的对端套接字。如果这个条件成⽴,那么就不能向 other 套接字发送数据,因为 other 并不是当前套接字的对端套接字,这种情况下发送数据可能会引发错误或者产⽣不确定的结果

unix_peer() 函数尝试返回指向当前 Unix Domain 套接字的对端套接字的指针,如果当前套接字不是连接状态或者没有对端套接字则返回空指针。该函数通常⽤于判断当前
Unix Domain 套接字是否有对端套接字,以决定是否可以进⾏数据发送。

  1. other!=sk, 因为 other=unix_peer_get(sk)(其实就是other=sk->peer) ,该条件意味着 sk->peer!=sk,在域套接字中 sk代表通信的⼀端,sk->peer代表通信的另⼀端,该条件是为了避免循环引⽤。本⽂档中默认 sk是客⼾端,other是服务端。
  2. unlikely(unix_peer(other)!=sk&&unix_recvq_full_lockless(other)),⾸先 unix_peer(other)!=sk意味着 sk->peer->peer!=sk说明 sk客⼾端所指向的服务端发⽣了变化(⽐如在客⼾端发送的过程中⼜有⼀个新的客⼾端与服务端建⽴了连接),其次是 unix_recvq_full_lockless(other)如下⾯代码所⽰,当条件满⾜时代表着 other服务端接收队列深度⼤于sk_max_ack_backlog
af_unix.c:
static inline int unix_recvq_full_lockless(conststructsock*sk)
{
	return skb_queue_len_lockless(&sk->sk_receive_queue)>
		READ_ONCE(sk->sk_max_ack_backlog);
}
  1. unix_peer(sk)!=other||unix_dgram_peer_wake_me(sk,other)unix_peer(sk)!=other⽤于判断当前 Unix Domain 套接字( sk )是否为另⼀个 Unix Domain 套接字(other )的对端套接字,这⾥只能是other发⽣了变化 ;在 unix_dgram_peer_wake_me中只有other端接收队列深度⼤于 sk_max_ack_backlog时才会 return 1
af_unix.c:
static inline int unix_recvq_full(conststructsock*sk)
{
	return skb_queue_len(&sk->sk_receive_queue) > sk->sk_max_ack_backlog;
}
static int unix_dgram_peer_wake_me(structsock*sk, structsock*other)
{
	int connected;
	connected = unix_dgram_peer_wake_connect(sk,other);
	if(unix_recvq_full(other))
		return 1;
	if(connected)
		unix_dgram_peer_wake_disconnect(sk,other);
	return 0;
}

总结

域套接字sendto errno -11存在以下可能:

  1. socket发送缓冲区满(可复现)。
  2. other的对端不是sk(本客⼾端)并且 unix_recvq_full_lockless成⽴
    2.1 sk(本客⼾端)的对端也不是other。
    2.2 other接收队列深度⼤于 sk_max_ack_backlog(可复现)。

条件2中 unix_recvq_full_lockless,代表other接收队列深度⼤于 sk_max_ack_backlog,不过这⾥ unix_recvq_full_lockless调⽤的是 skb_queue_len_lockless是不加锁的,因此这⾥存在不确定性,但⾄少内核得到的信息是other接收队列深度⼤于 sk_max_ack_backlog

条件2.1和2.2成⽴的前提是条件2先成⽴。
针对条件2.1成⽴的可能性:
1)sk 与 other 建链,此时 sk->peer==other,other==sk->peer
2)new_sk(新的客⼾端)与other建链,此时 sk->peer==other,other->peer!=sk,other->peer==new_sk,new_sk->peer==other
3)new_sk⾼速发消息到other使 unix_recvq_full_lockless条件满⾜。
4)sk发消息进⼊unix_dgram_sendmsg内部并到达unlikely(unix_peer(other)!=sk&&unix_recvq_full_lockless(other))之后并且在 unix_peer(sk)!=other|| unix_dgram_peer_wake_me(sk,other)之前的位置时,other端重新初始化了,条件 unix_peer(sk)!=other满⾜。

在sendto的过程中重新初始化other,⽬前没有很好的复现⽅法。

当客⼾环境并没有使⽤ socketpairconnect ,那么sk->peer和other->peer并没有相互引⽤,并且 other->peer==NULL。因此other的对端不是sk(本客⼾端)并且sk(本客⼾端)的对端
也不是other,在这种情况下当other接收队列深度⼤于 sk_max_ack_backlog时,将返回 EAGAIN(error -11)。
事实上,unix_dgram_sendto返回 EAGAIN时,要么 socket发送缓冲区满 ,要么 other接收队列深度⼤于sk_max_ack_backlog,因为如果第1个if不成⽴(条件2),那么第2个if也不会成⽴(条件2.1、条件2.2)。
域套接字sendto errno -11分析

调试手段

#sysctl-a|grepunix
net.unix.max_dgram_qlen=512
#sudosysctl-a|grep"net.core.wmem"
net.core.wmem_default=2097152
net.core.wmem_max=2097152

net.unix.max_dgram_qlen 代表缓冲区队列深度(缓冲区中有多少个数据包)
net.core.wmem_max 代表缓冲区最⼤⼤小(所有数据包的总⻓度)
针对errno -11, 可适当增加
net.unix.max_dgram_qlen 的⼤小。

发送缓冲区溢出判断

#include<sys/ioctl.h>
#include<linux/sockios.h>
long outq=0;
ioctl(sockfd,SIOCOUTQ,&outq);

errno -11 前后可根据该代码获取发送缓冲区的⼤小并与net.core.wmem_max 对⽐。

kprobe 监控socket 缓冲区是否溢出

kprobe监控sock_alloc_send_pskb、unix_dgram_peer_wake_me


     add_kprobe_event 'r:probeE1 sock_alloc_send_pskb $retval'
     set_kprobe_event_filter 'arg1 == 0' probeE1
     add_kprobe_event 'r:probeE2 unix_dgram_peer_wake_me $retval'
     set_kprobe_event_filter 'arg1 == 1' probeE2
     add_kprobe_event 'r:probeE3 unix_dgram_sendmsg $retval'
     set_kprobe_event_filter 'arg1 == 0xfffffff5' probeE3

     enable_trace_event 'sock/sock_rcvqueue_full'
     enable_trace_event 'sock/sock_exceed_buf_limit'

skb_queue_len 和 skb_queue_len_lockless区别

在 Linux 内核中,skb_queue_len 和 skb_queue_len_lockless 都是⽤于获取 sk_buff 队列⻓度的函数。这两个函数的差异在于函数调⽤时是否⽀持锁机制,具体描述如下:
skb_queue_len() 是⼀个基于锁的队列⻓度计算函数,当需要获取 sk_buff 队列⻓度时,该函数会获取队列的⾃旋锁来保证队列在计算期间不发⽣并发修改。因为该函数对队列
实⾏锁机制,当需要查询 sk_buff 队列⻓度时可能会受到锁的竞争和等待时延等因素的影响。
skb_queue_len_lockless() 是⼀个不⽀持锁的队列⻓度计算函数,该函数可以在不加锁的情况下,快速获取 sk_buff 队列的实际⻓度。由于该函数不需要获取队列的锁,因此其
执⾏速度快,但也可能在并发写⼊数据时因为⽆法保证数据⼀致性而产⽣错误的队列⻓度计算结果。
根据内核实现,当我们希望检查队列的⻓度时,⼀般建议使⽤ skb_queue_len 而不是 skb_queue_len_lockless ,因为前者可以确保计算结果的准确性,并通过⾃旋锁确保计算
过程不受并发修改的影响。而后者则主要⽤于在不需要确保 sk_buff 队列准确性的情况下快速计算其⻓度,⽐如有些地⽅例如数据处理中可能观察者只需要⼀个近似值,⽤
skb_queue_len_lockless() 可以显著降低系统的开销。

unix_socketpair

Unix 域套接字提供了⼀种可靠的进程间通信机制,其中 unix_socketpair 是其中的⼀个函数。该函数⽤于创建⼀对相互连接的套接字,这两个套接字可以⽤于进程间的通信。
具体来说,unix_socketpair 函数创建⼀对套接字(⼜称为 socket pair),这两个套接字在创建时已经互相连接。这意味着,对其中⼀个套接字进⾏任何操作都会直接影响另⼀个
套接字。因此,可以使⽤这对套接字进⾏进程间通信,⽐如在两个进程之间传递⽂件描述符、管道、消息等。
两个套接字在创建时有以下特点:
套接字对是由内核创建的,不依赖于⽂件系统中存在的⽂件。
套接字对是⼀对⼀的,即⼀个套接字只能被⼀个进程所拥有,而另⼀个套接字只能被另⼀个进程所拥有。
套接字对是双向的,即它们既可以⽤于读也可以⽤于写。
套接字对是数据传输的最小单位,因此数据交换的时候也是传输整块数据,不能传输部分数据。
unix_socketpair 函数的调⽤⽅式如下:
#include <sys/socket.h>
int socketpair(int domain, int type, int protocol, int sv[2]);
其中 domain 参数⽤于指定套接字协议簇,type 参数⽤于指定套接字类型,protocol 参数⽤于指定协议类型。sv 参数是⼀个已经分配好的数组,⽤于返回 socket pair 的描述
符。调⽤成功将返回 0,否则返回 -1。

connect操作

在 Unix 域套接字中,unix_dgram_connect 函数⽤于建⽴连接并指定该连接的⽬标套接字地址。该函数主要⽤于 Datagram 套接字的客⼾端,能够为该套接字指定⼀个默认
的⽬标地址,以便在后续的发送操作中⽆需再指定⽬标地址。
unix_dgram_connect 函数的具体作⽤如下:
建⽴连接。连接建⽴后,套接字就可以直接向指定的⽬标地址发送数据,不再需要每次都指定⽬标地址。
指定默认⽬标地址。连接建⽴后,可以使⽤ unix_dgram_sendmsg 等函数向默认⽬标地址发送数据。
接收数据。经过 unix_dgram_connect 函数连接的套接字也可以接收从指定的⽬标地址发送过来的数据。
请注意,unix_dgram_connect 函数并不是必须的,如果在套接字操作中指定了⽬标地址,则会⾃动建⽴连接。但是通过 unix_dgram_connect 函数建⽴连接可以使得发送数
据等操作更加⽅便。
unix_dgram_connect 函数的函数原型如下:
#include <sys/socket.h>
int unix_dgram_connect(int sockfd, const struct sockaddr_un* addr, socklen_t addrlen);
其中 sockfd 参数是待连接套接字的⽂件描述符,addr 参数是⽬标套接字地址,addrlen 参数是⽬标套接字地址的⻓度。调⽤成功将返回 0,否则返回 -1。

sendto
sendto时如果other不存在,则会主动通过地址和路径去寻找other,然后通过 unix_may_send是否可以发送,允许发送的条件是 other->peer==NULL or other->peer==sk
域套接字sendto errno -11分析文章来源地址https://www.toymoban.com/news/detail-679769.html

到了这里,关于域套接字sendto errno -11分析的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 网络编程之 Socket 套接字(使用数据报套接字和流套接字分别实现一个小程序(附源码))

    网络编程是指网络上的主机,通过不同的进程,以编程的方式实现 网络通信(或称为网络数据传输) 只要满足不同的进程就可以进行通信,所以即便是在同一个主机,只要不同的进程,基于网络传输数据,也属于网络编程 在一次网络传输中: 发送端: 数据的 发送方进程

    2024年02月03日
    浏览(63)
  • 【JavaEE】网络编程之TCP套接字、UDP套接字

    目录 1.网络编程的基本概念 1.1为什么需要网络编程  1.2服务端与用户端 1.3网络编程五元组  1.4套接字的概念 2.UDP套接字编程 2.1UDP套接字的特点  2.2UDP套接字API 2.2.1DatagramSocket类 2.2.2DatagramPacket类  2.2.3基于UDP的回显程序 2.2.4基于UDP的单词查询  3.TCP套接字编程 3.1TCP套接字的特

    2023年04月20日
    浏览(75)
  • 【JaveEE】网络编程之TCP套接字、UDP套接字

    目录 1.网络编程的基本概念 1.1为什么需要网络编程  1.2服务端与用户端 1.3网络编程五元组  1.4套接字的概念 2.UDP套接字编程 2.1UDP套接字的特点  2.2UDP套接字API 2.2.1DatagramSocket类 2.2.2DatagramPacket类  2.2.3基于UDP的回显程序 2.2.4基于UDP的单词查询  3.TCP套接字编程 3.1TCP套接字的特

    2023年04月13日
    浏览(169)
  • 网络编程套接字(3)——Java数据报套接字(UDP协议)

    目录 一、Java数据报套接字通信模型 二、UDP数据报套接字编程 1、DatagramSocket         (1)DatagramSocket构造方法         (2)DatagramSocket方法 2、DatagramPacket         (1)DatagramPacket构造方法         (2)DatagramPacket方法 3、InetSocketAddress 三、代码示例:回显服务

    2024年03月12日
    浏览(99)
  • 【C/C++套接字编程】套接字的基本概念与基础语法

    TCP/UDP实验为牵引,学习套接字编程的相关知识,再进一步深化对TCP/UDP的理解 目录 前言 Socket编程语法 1. 套接字及创建 什么是套接字? 创建套接字 2. 端口绑定 3. 收发信息 与recv()函数的比较: 与send()函数的比较: 编程实例  总结 系列博客 【C/C++套接字编程】TCP协议通信的

    2024年02月09日
    浏览(47)
  • Python进阶篇(三)-- TCP套接字与UDP套接字编程

    1.1 介绍         本文将首先利用 Python 实现面向TCP连接的套接字编程基础知识:如何创建套接字,将其绑定到特定的地址和端口,以及发送和接收数据包。其次还将学习 HTTP 协议格式的相关知识。在此基础上,本篇将用 Python 语言开发一个简单的 Web 服务器,它仅能处理一

    2023年04月23日
    浏览(43)
  • 【Python】Python 网络编程 ( Socket 套接字简介 | Socket 套接字使用步骤 | Socket 套接字服务端与客户端开发 )

    Socket 套接字 是一种 进程之间的 通信机制 , 通过套接字可以在 不同的进程之间 进行数据交换 ; 在 网络编程 中 , Socket 套接字 主要用于 客户端 与 服务器 之间的 通信 , 大部分 网络相关的应用程序 , 都使用到了 Socket 套接字技术 ; 套接字有两种类型 : 流套接字 : 提供了一个可

    2024年02月15日
    浏览(153)
  • 兼容流式套接字与数据报 套接字的回射服务器

    实验流程: 1)设计基于select模型的双协议服务器方案 创建套接字:为每种协议创建一个套接字(例如,TCP和UDP)。 绑定套接字:将套接字绑定到指定的端口上。 设置套接字选项:为每个套接字设置相应的选项,例如允许重用地址、设置超时时间等。 监听套接字:对于TCP套

    2024年04月16日
    浏览(50)
  • 本地套接字(domain)

            本地套接字是一种特殊类型的套接字,和 TCP/UDP 套接字不同。TCP/UDP 即使在本地地址通信,也要走系统网络协议栈,而本地套接字,严格意义上说提供了一种单主机跨进程间调用的手段,减少了协议栈实现的复杂度,效率比 TCP/UDP 套接字都要高许多。本地套接字是

    2023年04月23日
    浏览(26)
  • 本地字节流套接字

    字节流本地套接字服务器完整代码如下: 接下来解析一下: 这段代码创建了一个字节流本地套接字,AF_LOCAL表示类型可以认为与AF_UNIX等同,SOCK_STREAM表明使用TCP,即数据流。 删除local_path对应的文件,这样可以保持幂等性。 把文件绑定到套接字上,然后监听文件。 接收服务

    2024年02月07日
    浏览(30)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包