linux ipv4 代码分析系列(二)
作者:
在(一)中分析了inet_init的初始化部分,但在这之前还是有一部分的初始化需要说明一下,这个就是/net/socket.c/sock_init()。它的初始化在inet_init之前,详解对应函数的定义。
core_initcall(sock_init); /* early initcall */ fs_initcall(inet_init); #define core_initcall(fn) __define_initcall("1",fn,1) #define fs_initcall(fn) __define_initcall("5",fn,5)
|
下面简单分析一下sock_init
static int __init sock_init(void) { /* * Initialize sock SLAB cache. */ sk_init(); /* * Initialize skbuff SLAB cache */ skb_init(); //这个函数的初始化比较有意思,因为它的内部申请了两个内存头节点。为了说明这个用处,给出一个链接,一句话还是不能说清楚的,内存管理的确比较复杂。 https://www.ibm.com/developerworks/cn/linux/l-linux-slab-allocator/
/* * Initialize the protocols module. */ init_inodecache(); //同上 register_filesystem(&sock_fs_type); sock_mnt = kern_mount(&sock_fs_type);//这里添加了socket文件系统的支持,可以cat /proc/filesystems查看当前支持多少种文件系统,其中的”nodev sockfs”就是要注册的文件系统。注册socket文件系统的好处就是可以像操作文件一样进行发送接收数据。看看这个文件: srw-rw---- 1 root admin 0 Apr 20 03:46 networkd_uds
/* The real protocol initialization is performed in later initcalls. */
//下面这个功能就是大名鼎鼎的netfilter了,虽然是可选的组件,但现在的内核默认都是编译到内核中了,见arch/xx/xx_defconfig文件 CONFIG_NETFILTER=y Netfilter功能强大, 支持L2,L3,L4层的包过滤,数据包修改,NAT,转发策略,拦截策略。另外可以自己写module实现很多其他的功能,比如xt_limit module能够实现简单的流量控制等功能。好东西啊。
#ifdef CONFIG_NETFILTER netfilter_init(); #endif return 0; }
|
/net/socket.c这个文件结构很清晰,也比较好分析,都说socket编程,多少也应该了解一下实现吧。
SYSCALL_DEFINE3(socket, int, family, int, type, int, protocol)
首先SYSCALL_DEFINE3这个宏定义了socket系统调用的实现。而EXPORT_SYMBOL修饰的函数是供内核其他模块使用的。
SYSCALL_DEFINE3通过跟踪不难发现最终定义了sys_socket系统调用,并加入系统调用数组sys_call_table中,linux的宏定义还是比较烦的,估计玩过连连看的兄弟应该能很快的找到定义:) a)#define SYSCALL_DEFINE3(name, ...) SYSCALL_DEFINEx(3, _##name, __VA_ARGS__) b)#define SYSCALL_DEFINEx(x, sname, ...) \ __SYSCALL_DEFINEx(x, sname, __VA_ARGS__) c)asmlinkage long sys##name(__SC_DECL##x(__VA_ARGS__)) 再看include/asm-generic/unistd.h #define __NR_socket 198 __SYSCALL(__NR_socket, sys_socket)
|
SYSCALL_DEFINE3(socket, int, family, int, type, int, protocol)
SYSCALL_DEFINE3(socket, int, family, int, type, int, protocol) { int retval; struct socket *sock; int flags;
/* Check the SOCK_* constants for consistency. */ BUILD_BUG_ON(SOCK_CLOEXEC != O_CLOEXEC); BUILD_BUG_ON((SOCK_MAX | SOCK_TYPE_MASK) != SOCK_TYPE_MASK); BUILD_BUG_ON(SOCK_CLOEXEC & SOCK_TYPE_MASK); BUILD_BUG_ON(SOCK_NONBLOCK & SOCK_TYPE_MASK);
flags = type & ~SOCK_TYPE_MASK; if (flags & ~(SOCK_CLOEXEC | SOCK_NONBLOCK)) return -EINVAL; type &= SOCK_TYPE_MASK;
if (SOCK_NONBLOCK != O_NONBLOCK && (flags & SOCK_NONBLOCK)) flags = (flags & ~SOCK_NONBLOCK) | O_NONBLOCK;
//sock_create->sock_alloc->new_inode 通过调用关系可以看出,这个函数的本质是申请一个inode节点;同时还需要注意的是,在__sock_create函数中调用了函数指针int (*create)(struct net *net, struct socket *sock, int protocol),用于各类型socket初始化及函数指针挂接。 retval = sock_create(family, type, protocol, &sock);
if (retval < 0) goto out;
retval = sock_map_fd(sock, flags & (O_CLOEXEC | O_NONBLOCK)); //在当前进程中找到一个空闲的fd,把fd和inode关联起来。 if (retval < 0) goto out_release;
out: /* It may be already another descriptor 8) Not kernel problem. */ return retval;
out_release: sock_release(sock); return retval; }
|
至于sys_bind, sys_listen, sys_connect, sys_send, sys_recv等相关函数这里并没有具体实现,因为socket类型有多种,这里基于可扩展性考虑,根据socket传入参数来定位socket类型,进而调用对应的函数指针。
sys_accept在这里的实现类似sys_socket。
socket类型的注册和卸载见这两个函数,
int sock_register(const struct net_proto_family *ops)
void sock_unregister(int family)
另外还有一些供kernel调用的函数见kernel_****,开头的函数。
由于目前只考虑ipv4的实现,那么在net/ipv4/下,调用sock_register的地方是在(一)中分析的inet_init函数里调用的。那么我们在用户进程中调用socket函数时,kernel调用了/net/ipv4/af_inet.c/inet_create函数,看看inet_create都干了什么。
/* * 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;
if (unlikely(!inet_ehash_secret)) if (sock->type != SOCK_RAW && sock->type != SOCK_DGRAM) build_ehash_secret();
sock->state = SS_UNCONNECTED;//设置socket状态
/* Look for the requested type/protocol pair. */ lookup_protocol: err = -ESOCKTNOSUPPORT; rcu_read_lock();
//下面这部分片段定位到inet的socket子类型,TCP/UDP/IP 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; }
if (unlikely(err)) {//这里面有一个自动加载的功能,如果功能模块在编译时指定为独立模块,kernel会按照下面的命名规则找到对应模块,并加载到内核当中。 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; if (answer->capability > 0 && !capable(answer->capability)) goto out_rcu_unlock;
err = -EAFNOSUPPORT; if (!inet_netns_ok(net, protocol)) goto out_rcu_unlock;
sock->ops = answer->ops; //这里挂接的函数指针。 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结构分配内存,并初始化里面的各字段。每个字段的含义没有必要都要搞清楚 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; 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) { err = sk->sk_prot->init(sk); if (err) sk_common_release(sk); } out: return err; out_rcu_unlock: rcu_read_unlock(); goto out; }
|
阅读(2871) | 评论(0) | 转发(0) |