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)
将数据放?了对端的接收队列中。
第1处
sock_alloc_send_pskb
函数中当socket发送缓冲区满时( sk_wmem_alloc_get(sk)>=sk->sk_sndbuf
)将返回 EAGAIN。
第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 套接字是否有对端套接字,以决定是否可以进?数据发送。
other!=sk
, 因为other=unix_peer_get(sk)
(其实就是other=sk->peer
) ,该条件意味着sk->peer!=sk
,在域套接字中 sk代表通信的?端,sk->peer代表通信的另?端,该条件是为了避免循环引?。本?档中默认 sk是客?端,other是服务端。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);
}
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存在以下可能:
- socket发送缓冲区满(可复现)。
- 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_r