追踪Linux TCPIP代码运行,01.追踪linux代码运行——socket创建

[前言]

一直想好好研究linux的网络实现,又不知从何入手,总是片面的学习,形不成体系。最近发现秦健老师的《追踪Linux TCP/IP代码运行》,觉得是个不错的入口点,于是决定借此一来整合一下自己零碎的知识,二来希望对节后的工作有些帮助,何乐而不为。本文仅仅是介绍从调用socket函数到成功返回一个socket文件描述符之间所涉及到的重要函数以及其调用关系、代码位置等,该过程涉及的重要数据结构以及相关知识会有专门的介绍。

[linux内核版本:3.10.25]

[一]从GLIBC封装的库函数到系统调用

我们平常在网络编程中调用的socket()函数,实为GLIBC封装的库函数,该库函数实际功能采用汇编语言实现,最后通过0x80号软中断陷入内核,调用真正的系统调用程序。

|>>socket():

|创建一个socket套接字

|int socket(int domain, int type, int protocol);

|glibc-2.20/sysdeps/unix/sysv/linux/i386/socket.S[43]

---|>> int $0x80:

---|0x80号软中断用来陷入内核,然后调用系统调用的总入口system_call

---|---|>> system_call():

---|---|系统调用的总入口,完全用汇编语言实现

---|---|asmlinkage int system_call(void);

---|---|linux-3.10.25/arch/x86/kernel/entry_64.S[608]

---|---|---|>>

sys_socketcall():

---|---|---|内核提供给socket通信的总入口

---|---|---|asmlinkage long sys_socketcall(int call,

unsigned long __user *args);

---|---|---|linux-3.10.25/net/socket.c[2456]

点击(此处)折叠或打开

SYSCALL_DEFINE2(socketcall, int, call, unsigned long __user *, args)

{

......

/* copy_from_user should be SMP safe. */

if (copy_from_user(a, args, len))

return -EFAULT;

......

switch (call) {

case SYS_SOCKET:

err = sys_socket(a0, a1, a[2]);

break;

case SYS_BIND:

err = sys_bind(a0, (struct sockaddr __user *)a1, a[2]);

break;

case SYS_CONNECT:

err = sys_connect(a0, (struct sockaddr __user *)a1, a[2]);

break;

case SYS_LISTEN:

err = sys_listen(a0, a1);

break;

case SYS_ACCEPT:

err = sys_accept4(a0, (struct sockaddr __user *)a1,

(int __user *)a[2], 0);

break;

[二]调用sys_socket创建socket和sock结构体

在Linux-3.10.25中socket系列函数(sys_xxx)在内核源码中都是用DEFINEX宏来定义,关于DEFINEX的定义请查看linux-3.10.25/include/linux/syscalls.h[170],至于为什么这样做不是我们关心的重点,使用这些宏转换后,这些函数的真面目与2.6系列内核中的定义没有什么不同,或者我们也可以直接查看这些函数在头文件中的声明来确定这些函数的原型。

|>>sys_socket():

|asmlinkage long

sys_socket(int family, int type, int protocol);

|SYSCALL_DEFINE3(socket,

int, family, int, type, int, protocol)

|linux-3.10.25/net/socket.c[1361]

---|>>

sock_create():

---|直接调用__scok_create()函数执行创建任务

---|int

sock_create(int family, int type, int protocol, struct socket **res)

---|linux-3.10.25/net/socket.c[1349]

---|---|>>  __sock_create():

---|---|创建socket和sock结构体

---|---|int

__sock_create(struct net *net, int family, int type, int protocol, struct

socket **res, int kern)

---|---|linux-3.10.25/net/socket.c[1236]

---|---|---|>> sock_alloc() :

---|---|---|创建socket结构体

---|---|---|static

struct socket *sock_alloc(void)

---|---|---|linux-3.10.25/net/socket.c[531]

---|---|---|>>inet_create():

---|---|---|创建scok结构体,在__scok_create函数中的1311行被调用

---|---|---|static

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

kern)

---|---|---|linux-3.10.25/net/ipv4/af_inet.c[275]

在__sock_create()函数中linux系统创建了两个重要的数据结构,socket和sock结构体。Sock结构体根据使用的具体协议挂入socket结构体中,同时sock结构体中又包含了指向sk_buff结构的指针。sk_buff这个结构相信大家并不陌生,每个协议都是用该结构体来封装、运载数据包,也就说每一个数据包都用一个sk_buff结构体来表示。

[三]调用sock_alloc()函数创建socket结构体

|>>

sock_alloc() :

|创建socket结构体

|static struct socket *sock_alloc(void)

|linux-3.10.25/net/socket.c[531]

---|>> new_inode_pseudo():

---|给指定的超级块结构申请一个inode

---|struct inode

*new_inode_pseudo(struct super_block *sb)

---| linux-3.10.25/fs/inode.c[904]

---|---|>> alloc_inode():

---|---|给指定的超级块结构申请一个inode

---|---|static

struct inode *alloc_inode(struct super_block *sb)

---|---|linux-3.10.25/fs/inode.c[202]

---|---|---|>>sock_alloc_inode() :

---|---|---|给指定的超级块结构申请一个inode,在alloc_inode函数中207行被调用

---|---|---|static

struct inode *sock_alloc_inode(struct super_block *sb)

---|---|---|linux-3.10.25/net/socket.c[241]

点击(此处)折叠或打开

static struct inode *alloc_inode(struct super_block *sb)

{

struct inode *inode;

if (sb->s_op->alloc_inode)

/* 我们在这里调用sock_alloc_inode函数来创建socket和inode */

inode = sb->s_op->alloc_inode(sb);

else

inode = kmem_cache_alloc(inode_cachep, GFP_KERNEL);

点击(此处)折叠或打开

/* linux-3.10.25/net/socket.c[299] */

static const struct super_operations sockfs_ops = {

.alloc_inode    = sock_alloc_inode,

.destroy_inode    = sock_destroy_inode,

.statfs        = simple_statfs,

}

点击(此处)折叠或打开

/* linux-3.10.25/net/socket.c[241] */

static struct inode *sock_alloc_inode(struct super_block *sb)

{

struct socket_alloc *ei;

struct socket_wq *wq;

ei = kmem_cache_alloc(sock_inode_cachep, GFP_KERNEL);

if (!ei)

return NULL;

wq = kmalloc(sizeof(*wq), GFP_KERNEL);

if (!wq) {

kmem_cache_free(sock_inode_cachep, ei);

return NULL;

}

init_waitqueue_head(&wq->wait);

wq->fasync_list = NULL;

RCU_INIT_POINTER(ei->socket.wq, wq);

ei->socket.state = SS_UNCONNECTED;

ei->socket.flags = 0;

ei->socket.ops = NULL;

ei->socket.sk = NULL;

ei->socket.file = NULL;

return &ei->vfs_inode;

}

[四]调用inet_create()函数创建sock结构体

|>> inet_create() :

|创建并设置sock结构体,在__sock_create函数中1311行被调用

|static int

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

|linux-3.10.25/net/ipv4/af_inet.c[275]

---|>>sk_alloc():

---|创建并初始化sock结构体

---|struct sock

*sk_alloc(struct net *net, int family, gfp_t priority, struct proto *prot)

---| linux-3.10.25/core/scok.c[1314]

---|---|>>sk_prot_alloc():

---|---|创建socket结构体

---|---|static

struct sock *sk_prot_alloc(struct proto *prot, gfp_t priority, int family)

---|---|linux-3.10.25/core/scok.c[1225]

点击(此处)折叠或打开

/* 在这里我们创建sock结构体 */

err = pf->create(net, sock, protocol, kern);

if (err < 0)

goto out_module_put;

点击(此处)折叠或打开

/* linux-3.10.25/net/ipv4/af_inet.c[1019] */

static const struct net_proto_family inet_family_ops = {

.family = PF_INET,

.create = inet_create,

.owner    = THIS_MODULE,

};

点击(此处)折叠或打开

/* linux-3.10.25/net/ipv4/af_inet.c[275] */

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

int kern)

{

struct sock *sk;

点击(此处)折叠或打开

struct sock *sk_alloc(struct net *net, int family, gfp_t priority,

struct proto *prot)

{

struct sock *sk;

sk = sk_prot_alloc(prot, priority | __GFP_ZERO, family);

if (sk) {

/* 如果sock结构体分配成功那么将在该函数做一些初始化工作 */

sk->sk_family = family;

/*

* See comment in struct sock definition to understand

* why we need sk_prot_creator -acme

*/

sk->sk_prot = sk->sk_prot_creator = prot;

sock_lock_init(sk);

sock_net_set(sk, get_net(net));

atomic_set(&sk->sk_wmem_alloc, 1);

sock_update_classid(sk);

sock_update_netprioidx(sk);

}

return sk;

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值