sys接口linux编程,网络编程常用接口的内核实现----sys_socket()

本文详细探讨了Linux内核中的sys_socket()函数,该函数在创建套接字时,主要调用sock_create()和sock_map_fd()。重点讲解了sock_create()的实现,包括如何通过__sock_create()分配inode和socket对象,以及对于TCP套接字如何调用inet_create()进行进一步处理。inet_create()中涉及到inet_protosw列表的查找、权限检查以及模块加载等关键步骤。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

sys_socket()的代码流程图如下所示:

cd01184cd8031792f16b18dfb5eaf726.png

从上面的代码及流程图中,可以看到sys_socket()主要调用sock_create()和sock_map_fd()来完成创建套接字的工作,在讨论这两个函数之前,要强调一下type参数。从linux 2.6.27开始,参数type除了指定套接字类型外,还可以通过或运算来指定SOCK_CLOEXEC和SOCK_NONBLOCK标志来改变套接字的行为。可以通过man socket命令来查看,原文是:

Since  Linux  2.6.27, the type argument serves a second purpose: in addition to specifying a

socket type, it may include the bitwise OR of any of the following  values,  to  modify  the

behavior of socket():

SOCK_NONBLOCK  Set the O_NONBLOCK file status flag on the new open file description.  Using

this flag saves extra calls to fcntl(2) to achieve the same result.

SOCK_CLOEXEC    Set the close-on-exec (FD_CLOEXEC) flag on the new file descriptor.  See the

description  of  the  O_CLOEXEC  flag in open(2) for reasons why this may be

useful.

一、sock_create()函数

sock_create()函数时对__sock_create()函数的简单封装,__sock_create()函数的代码流程图如下所示:

9a8de0a1bc5e70428a9e4c8b00a6e11e.png

__sock_create()调用sock_alloc()分配一个新的inode和socket对象,并设置套接字的权限位及套接字所属的用户和用户组。如果要创建的是TCP套接字,pf->create()中的pf是inet_family_ops变量,该变量的类型是net_proto_family结构,在net/ipv4/if_inet.c文件中定义。inet_family_ops的create成员设置的是inet_create()函数,因此如果是创建TCP套接字,pf->create()调用的实际是inet_create()函数。

inet_create()用来创建inet套接字,其源码及分析如下:

/*

* Create an inet socket.

*/

static int inet_create(struct net *net, struct socket *sock, int protocol)

{

struct sock *sk;

struct inet_protosw *answer;

struct inet_sock *inet;

struct proto *answer_prot;

unsigned char answer_flags;

char answer_no_check;

int try_loading_module = 0;

int err;

/*

* inet_ehash_secret用于在将除原始套接字和UDP套接字外的套接字,

* 哈希计算后插入到系统时用到的哈希因子,只设置一次。

*/

if (unlikely(!inet_ehash_secret))

if (sock->type != SOCK_RAW && sock->type != SOCK_DGRAM)

build_ehash_secret();

/*

* 设置套接字的初始状态。

*/

sock->state = SS_UNCONNECTED;

/* Look for the requested type/protocol pair. */

lookup_protocol:

err = -ESOCKTNOSUPPORT;

rcu_read_lock();

/*

* TCP套接字、UDP套接字、原始套接字的inet_protosw实例都

* 在inetsw_array数组中定义,这些实例会调用inet_register_protosw()

* 注册到inetsw中。

* 根据protocol查找要创建的套接字对应的四层传输协议。

*/

list_for_each_entry_rcu(answer, &inetsw[sock->type], list) {

err = 0;

/* Check the non-wild match. */

if (protocol == answer->protocol) {

if (protocol != IPPROTO_IP)

break;

} else {

/* Check for the two wild cases. */

if (IPPROTO_IP == protocol) {

protocol = answer->protocol;

break;

}

if (IPPROTO_IP == answer->protocol)

break;

}

err = -EPROTONOSUPPORT;

}

/*

* 如果没有找到,则调用request_module()来尝试加载

* 协议所属的模块,正常情况下不会发生。

*/

if (unlikely(err)) {

if (try_loading_module < 2) {

rcu_read_unlock();

/*

* Be more specific, e.g. net-pf-2-proto-132-type-1

* (net-pf-PF_INET-proto-IPPROTO_SCTP-type-SOCK_STREAM)

*/

if (++try_loading_module == 1)

request_module("net-pf-%d-proto-%d-type-%d",

PF_INET, protocol, sock->type);

/*

* Fall back to generic, e.g. net-pf-2-proto-132

* (net-pf-PF_INET-proto-IPPROTO_SCTP)

*/

else

request_module("net-pf-%d-proto-%d",

PF_INET, protocol);

goto lookup_protocol;

} else

goto out_rcu_unlock;

}

err = -EPERM;

/*

* 检查用户是否有创建指定协议类型的套接字的权限,

* 如果是TCP套接字,capability为-1,表示没有权限限制。

*/

if (answer->capability > 0 && !capable(answer->capability))

goto out_rcu_unlock;

err = -EAFNOSUPPORT;

/*

* 检查网络命名空间,检查指定的协议类型

* 是否已经添加,参见init_inet(),tcp协议对应

* 的net_protocol实例是tcp_protocol。

*/

if (!inet_netns_ok(net, protocol))

goto out_rcu_unlock;

/*

* 设置套接字的操作接口,如果是TCP协议,

* answer->ops的值为inet_stream_ops。

*/

sock->ops = answer->ops;

/*

* 获取套接字对应的proto实例,如果是TCP协议,

* 值为tcp_prot。

*/

answer_prot = answer->prot;

answer_no_check = answer->no_check;

answer_flags = answer->flags;

rcu_read_unlock();

WARN_ON(answer_prot->slab == NULL);

err = -ENOBUFS;

/*

* 分配sock实例并初始化,如果是TCP协议,

* 则实际分配的大小为sizeof(tcp_sock)。

*/

sk = sk_alloc(net, PF_INET, GFP_KERNEL, answer_prot);

if (sk == NULL)

goto out;

err = 0;

/*

* 设置是否需要计算校验和的值

*/

sk->sk_no_check = answer_no_check;

if (INET_PROTOSW_REUSE & answer_flags)

sk->sk_reuse = 1;

inet = inet_sk(sk);

/*

* 设置是否是基于连接的套接字

*/

inet->is_icsk = (INET_PROTOSW_ICSK & answer_flags) != 0;

if (SOCK_RAW == sock->type) {

inet->num = protocol;

if (IPPROTO_RAW == protocol)

inet->hdrincl = 1;

}

/*

* 后面都是对一些成员的初始化,不作过多的

* 分析。

*/

if (ipv4_config.no_pmtu_disc)

inet->pmtudisc = IP_PMTUDISC_DONT;

else

inet->pmtudisc = IP_PMTUDISC_WANT;

inet->id = 0;

sock_init_data(sock, sk);

/*

* 在释放传输控制块之前释放资源。

*/

sk->sk_destruct    = inet_sock_destruct;

sk->sk_protocol    = protocol;

/*

* 在TCP中,用于接收预备队列和后备队列中的

* TCP段,实际的函数时tcp_v4_do_rcv()。

*/

sk->sk_backlog_rcv = sk->sk_prot->backlog_rcv;

inet->uc_ttl = -1;

inet->mc_loop = 1;

inet->mc_ttl = 1;

inet->mc_all = 1;

inet->mc_index = 0;

inet->mc_list = NULL;

sk_refcnt_debug_inc(sk);

if (inet->num) {

/* It assumes that any protocol which allows

* the user to assign a number at socket

* creation time automatically

* shares.

*/

inet->sport = htons(inet->num);

/* Add to protocol hash chains. */

sk->sk_prot->hash(sk);

}

if (sk->sk_prot->init) {

/*

* TCP协议调用的tcp_v4_init_sock()。

*/

err = sk->sk_prot->init(sk);

if (err)

sk_common_release(sk);

}

out:

return err;

out_rcu_unlock:

rcu_read_unlock();

goto out;

}0b1331709591d260c1c78e86d0c51c18.png

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值