文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

Linux 内核网络之 connect 的实现

2024-11-30 17:49

关注

对于面向连接的协议,如 TCP, connect() 建立一条与指定的外部地址的连接。若在connect调用之前没有绑定地址和端口,则会自动绑定一个地址和端口号套接口。

asmlinkage long sys_connect(int fd, struct sockaddr __user *uservaddr,
int addrlen)
{
struct socket *sock;
char address[MAX_SOCK_ADDR];
int err, fput_needed;
//根据文件描述符获取套接口指针,并且返回是否需要减少对文件引用计数标志。
sock = sockfd_lookup_light(fd, &err, &fput_needed);
if (!sock)
goto out;
//将用户空间的uservaddr数据复制到内核空间的address
err = move_addr_to_kernel(uservaddr, addrlen, address);
if (err < 0)
goto out_put;

err =
security_socket_connect(sock, (struct sockaddr *)address, addrlen);
if (err)
goto out_put;

//通过套接口系统调用的跳转表proto_ops,调用connect操作。TCP 中为 inet_stream_connect(), UDP 为 inet_dgram_connect()
err = sock->ops->connect(sock, (struct sockaddr *)address, addrlen,
sock->file->f_flags);
out_put:
// 根据fput_needed标志,调用fput_light减少对文件引用计数操作
fput_light(sock->file, fput_needed);
out:
return err;
}

通过套接口系统调用的跳转表 proto_ops ,调用 inet_stream_connect。

int inet_stream_connect(struct socket *sock, struct sockaddr *uaddr,
int addr_len, int flags)
{
struct sock *sk = sock->sk;
int err;
long timeo;

lock_sock(sk);

if (uaddr->sa_family == AF_UNSPEC) {

err = sk->sk_prot->disconnect(sk, flags);
sock->state = err ? SS_DISCONNECTING : SS_UNCONNECTED;
goto out;
}

switch (sock->state) {
default:
err = -EINVAL;
goto out;
case SS_CONNECTED:
err = -EISCONN;
goto out;
case SS_CONNECTING:
err = -EALREADY;

break;
case SS_UNCONNECTED:
err = -EISCONN;
if (sk->sk_state != TCP_CLOSE)
goto out;


err = sk->sk_prot->connect(sk, uaddr, addr_len);
if (err < 0)
goto out;


sock->state = SS_CONNECTING;


err = -EINPROGRESS;
break;
}


timeo = sock_sndtimeo(sk, flags & O_NONBLOCK);


if ((1 << sk->sk_state) & (TCPF_SYN_SENT | TCPF_SYN_RECV)) {


if (!timeo || !inet_wait_for_connect(sk, timeo))
goto out;

err = sock_intr_errno(timeo);

if (signal_pending(current))
goto out;
}


if (sk->sk_state == TCP_CLOSE)
goto sock_error;



sock->state = SS_CONNECTED;

err = 0;
out:
release_sock(sk);
return err;

sock_error:
err = sock_error(sk) ? : -ECONNABORTED;
sock->state = SS_UNCONNECTED;

if (sk->sk_prot->disconnect(sk, flags))
sock->state = SS_DISCONNECTING;
goto out;
}

inet_stream_connect() 主要做了以下事情:

对协议族进行检查。

此时套接口状态为 SS_UNCONNECTED, 调用 tcp_v4_connect() 来发送SYN包。

等待后续握手的完成:

1、如果socket是非阻塞的,那么就直接返回错误码 -EINPROGRESS。

2、如果socket为阻塞的,就调用 inet_wait_for_connect(),通过睡眠来等待。在以下三种情况下会被唤醒:

客户端调用tcp_v4_connect 发送SYN包时,设置客户端状态为 TCP_SYN_SENT。

进程休眠

static long inet_wait_for_connect(struct sock *sk, long timeo)
{

DEFINE_WAIT(wait);

prepare_to_wait(sk->sk_sleep, &wait, TASK_INTERRUPTIBLE);



while ((1 << sk->sk_state) & (TCPF_SYN_SENT | TCPF_SYN_RECV)) {
release_sock(sk);

timeo = schedule_timeout(timeo);

lock_sock(sk);

if (signal_pending(current) || !timeo)
break;

prepare_to_wait(sk->sk_sleep, &wait, TASK_INTERRUPTIBLE);
}

finish_wait(sk->sk_sleep, &wait);
return timeo;
}

当前进程加入到 socket 的等待队列 sk_sleep 中,然后进入休眠,直到超时或接收到信号。

进程被唤醒

在三次握手中,当客户端收到 SYN+ACK、发出ACK后,连接就成功建立了。此时连接的状态从TCP_SYN_SENT或TCP_SYN_RECV变成了 TCP_ESTABLISHED,表示连接建立成功。最终会调用 sock_def_wakeup() 来处理连接状态变化事件,唤醒进程,connect()成功返回。

调用过程如下

tcp_v4_rcv

-> tcp_v4_do_rcv

-> tcp_rcv_state_process

-> tcp_rcv_synsent_state_process

-> sk_wake_async(sk, 0, POLL_OUT);

static int tcp_rcv_synsent_state_process(struct sock *sk, struct sk_buff *skb,
struct tcphdr *th, unsigned len)
{
...

if (!sock_flag(sk, SOCK_DEAD)) {

sk->sk_state_change(sk);

sk_wake_async(sk, 0, POLL_OUT);
}

...
}

当链路建立成功后异步发送SIGIO信号,唤醒阻塞的进程并通知 socket 可写,这也就是为什么非阻塞调用 connect 时检查 socket 是否可写事件的原因。

static void sock_def_wakeup(struct sock *sk)
{
read_lock(&sk->sk_callback_lock);

if (sk->sk_sleep && waitqueue_active(sk->sk_sleep))

wake_up_interruptible_all(sk->sk_sleep);
read_unlock(&sk->sk_callback_lock);
}

最终调用 __wake_up_common(),由于nr_exclusive 为 0,因此会把此socket 上所有的等待进程都唤醒。

来源:今日头条内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯