为什么sctp_inet_listen struct socket*的第一个参数不是int

Why is the first parameter of sctp_inet_listen struct socket* instead of int

想研究一下内核网络部分的源码,了解内核的网络部分是如何实现的works.But在看listen函数的时候,发现了上面的问题。用man看到listen函数的第一个参数是int。 int listen(int sockfd, int 积压); 但是在https://github.com/torvalds/linux中,sctp_inet_listen函数的第一个参数是struct socket*,在protocol.c中我们知道listen是sctp_inet_listen

的函数指针
static const struct proto_ops inet_seqpacket_ops = {
    .family        = PF_INET,
    .owner         = THIS_MODULE,
    .release       = inet_release,  /* Needs to be wrapped... */
    .bind          = inet_bind,
    .connect       = sctp_inet_connect,
    .socketpair    = sock_no_socketpair,
    .accept        = inet_accept,
    .getname       = inet_getname,  /* Semantics are different.  */
    .poll          = sctp_poll,
    .ioctl         = inet_ioctl,
    .gettstamp     = sock_gettstamp,
    .listen        = sctp_inet_listen,
    .shutdown      = inet_shutdown, /* Looks harmless.  */
    .setsockopt    = sock_common_setsockopt, /* IP_SOL IP_OPTION is a problem */
    .getsockopt    = sock_common_getsockopt,
    .sendmsg       = inet_sendmsg,
    .recvmsg       = inet_recvmsg,
    .mmap          = sock_no_mmap,
    .sendpage      = sock_no_sendpage,
};
int sctp_inet_listen(struct socket *sock, int backlog)
{
    struct sock *sk = sock->sk;
    struct sctp_endpoint *ep = sctp_sk(sk)->ep;
    int err = -EINVAL;

    if (unlikely(backlog < 0))
        return err;

    lock_sock(sk);

    /* Peeled-off sockets are not allowed to listen().  */
    if (sctp_style(sk, UDP_HIGH_BANDWIDTH))
        goto out;

    if (sock->state != SS_UNCONNECTED)
        goto out;

    if (!sctp_sstate(sk, LISTENING) && !sctp_sstate(sk, CLOSED))
        goto out;

    /* If backlog is zero, disable listening. */
    if (!backlog) {
        if (sctp_sstate(sk, CLOSED))
            goto out;

        err = 0;
        sctp_unhash_endpoint(ep);
        sk->sk_state = SCTP_SS_CLOSED;
        if (sk->sk_reuse || sctp_sk(sk)->reuse)
            sctp_sk(sk)->bind_hash->fastreuse = 1;
        goto out;
    }

    /* If we are already listening, just update the backlog */
    if (sctp_sstate(sk, LISTENING))
        WRITE_ONCE(sk->sk_max_ack_backlog, backlog);
    else {
        err = sctp_listen_start(sk, backlog);
        if (err)
            goto out;
    }

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

在Linux中,C库函数listen(fd,backlog) corresponds to a syscall (SYS_listen) with the same prototype. This syscall is implemented in net/socket.c(见SYSCALL_DEFINE2(listen, int, fd, int, backlog))。它调用 net/socket.c:__sys_listen().

net/socket.c:__sys_listen() 通过查找文件描述 table 条目 fd,并做基本的检查和簿记工作。

struct socket 结构包含成员 ops,它是指向 struct proto_ops 的指针。这是一组函数指针,因此可以在同一接口中支持不同类型的套接字(例如,Unix 域套接字或 IP 套接字)。 (基本上每个套接字类型都定义了自己的 proto_ops。)

net/socket.c:__sys_listen() 得到那个set的listen函数指针,并调用它,这样不同的socket类型就可以实现自己的'listen' 设施。因为已经查找了文件描述符,并将其转换为指向套接字描述的指针,所以传递了该指针(而不是文件描述符)。 (相同或非常相似的接口用于所有 file/socket 使用函数的描述符。)

这里要认识到的核心点是文件描述符编号只是对文件描述引用的进程特定 table 的索引。对于套接字,该引用的类型为 struct sock *。 (文件描述的 table 是内核内部的,通常称为 file table;进程特定的 table of references 通常是称为 文件描述符 table;文件描述符是文件描述符 table 的索引。如果您觉得这令人困惑,请阅读例如维基百科 File descriptor 文章了解更多详情。)