Chinaunix首页 | 论坛 | 博客
  • 博客访问: 296381
  • 博文数量: 71
  • 博客积分: 30
  • 博客等级: 民兵
  • 技术积分: 217
  • 用 户 组: 普通用户
  • 注册时间: 2012-07-31 15:43
文章分类

全部博文(71)

文章存档

2016年(4)

2015年(2)

2014年(2)

2013年(63)

分类: LINUX

2013-06-14 16:42:49

我们看sk_alloc()函数的代码,其调用了sk_prot_alloc分配一个通用的sock结构体,有可能在高速缓冲中分配也可能在通用缓冲中分配,这个函数的代码并不算长

 

sys_socketcall()-->sys_socket()-->sock_create()-->__sock_create()-->通过pf->create()--> inet_create()-->sk_alloc()-->sk_prot_alloc()

static struct sock *sk_prot_alloc(struct proto *prot, gfp_t priority,
        int family)
{/* wumingxiaozu */
    struct sock *sk;
    struct kmem_cache *slab;

    slab = prot->slab;
    if (slab != NULL)
        sk = kmem_cache_alloc(slab, priority);
    else
        sk = kmalloc(prot->obj_size, priority);

    if (sk != NULL) {
        if (security_sk_alloc(sk, family, priority))
            goto out_free;

        if (!try_module_get(prot->owner))
            goto out_free_sec;
    }

    return sk;

out_free_sec:
    security_sk_free(sk);
out_free:
    if (slab != NULL)
        kmem_cache_free(slab, sk);
    else
        kfree(sk);
    return NULL;
}

如果知道内存管理中的高速缓存的朋友一定很清楚上面的slab的分配方法,kmem_cache_alloc()就是在专用的sock高速缓冲中分配我们要使用的sock结构,而kmalloc()函数则是通用的高速缓冲分配函数,至于这二个函数的具体过程与内存管理密不可分,所以这里如果没有了解或者学习过内核的朋友们,请参阅有关的操作系统理论中的内存管理章节内容,这些内容我们暂且不做说明了。分配成功会,会在sk_alloc()函数中接着对其进行初始化操作,调用sock_lock_init()函数对sock内部同步作用的sk_lock进行初始化

 

sys_socketcall()-->sys_socket()-->sock_create()-->__sock_create()-->通过pf->create()--> inet_create()-->sk_alloc()-->sock_lock_init()

static inline void sock_lock_init(struct sock *sk)
{/* wumingxiaozu */
    sock_lock_init_class_and_name(sk,
            af_family_slock_key_strings[sk->sk_family],
            af_family_slock_keys + sk->sk_family,
            af_family_key_strings[sk->sk_family],
            af_family_keys + sk->sk_family);
}

调用专用的宏sock_lock_init_class_and_name来完成声明并初始化sk_lock的内容

#define sock_lock_init_class_and_name(sk, sname, skey, name, key)     \
do {                                    \
    sk->sk_lock.owned = 0;                    \
    init_waitqueue_head(&sk->sk_lock.wq);                \
    spin_lock_init(&(sk)->sk_lock.slock);                \
    debug_check_no_locks_freed((void *)&(sk)->sk_lock,        \
            sizeof((sk)->sk_lock));                \
    lockdep_set_class_and_name(&(sk)->sk_lock.slock,        \
             (skey), (sname));                \
    lockdep_init_map(&(sk)->sk_lock.dep_map, (name), (key), 0);    \
} while (0)

关于细节我们也不追踪了,大多于初始化sk_lock相关。这个sk_lock是一个socket_lock_t结构变量,这是一个专门用于socket的锁结构

typedef struct {
    spinlock_t        slock;
    int            owned;
    wait_queue_head_t    wq;
    
/*
     * We express the mutex-alike socket_lock semantics
     * to the lock validator by explicitly managing
     * the slock as a lock variant (in addition to
     * the slock itself):
     */
wumingxiaozu
#ifdef CONFIG_DEBUG_LOCK_ALLOC
    struct lockdep_map dep_map;
#endif
} socket_lock_t;

我们看到其中包括了一个自旋锁slock,还有一个等待队列头wq,而上面的宏sock_lock_init_class_and_name正是对其内容的初始化设置。我们知道在__sock_create()函数的参数中从sock_create传递下来了current->nsproxy->net_ns,这是在进程的数据结构中task_struct中记录网络空间结构的指针,我们将会在后边进一步说明这个结构体struct net. sock_net_set()函数将sock结构中的sk_net记录下所属的net空间结构。get_net()是为了增加所属net结构的计数器。分配了sock结构并做了上述的初始化操作后,再回到inet_create函数中,代码进一步对sock的初始化,注意sksock结构,而socksocket结构。使用inet = inet_sk(sk);使sk赋值给struct inet_sock *inet结构变量,我们在unixsocket中曾经说了关于unix socketunix_sock,而这里用于INETsocket结构inet_sock。然后函数通过sock_init_data(sock, sk);sk进一步的初始化操作,并让socksk挂起钩来。

 

sys_socketcall()-->sys_socket()-->sock_create()-->__sock_create()-->通过pf->create()--> inet_create()-->sock_init_data()

void sock_init_data(struct socket *sock, struct sock *sk)
{
    skb_queue_head_init(&sk->sk_receive_queue);
    skb_queue_head_init(&sk->sk_write_queue);
    skb_queue_head_init(&sk->sk_error_queue);
#ifdef CONFIG_NET_DMA
    skb_queue_head_init(&sk->sk_async_wait_queue);
#endif

    sk->sk_send_head = NULL;/* wumingxiaozu */

    init_timer(&sk->sk_timer);

    sk->sk_allocation = GFP_KERNEL;
    sk->sk_rcvbuf = sysctl_rmem_default;
    sk->sk_sndbuf = sysctl_wmem_default;
    sk->sk_state = TCP_CLOSE;
    sk->sk_socket = sock;

    sock_set_flag(sk, SOCK_ZAPPED);

    if (sock) {
        sk->sk_type = sock->type;
        sk->sk_sleep = &sock->wait;
        sock->sk = sk;
    } else
        sk->sk_sleep = NULL;

    rwlock_init(&sk->sk_dst_lock);
    rwlock_init(&sk->sk_callback_lock);
    lockdep_set_class_and_name(&sk->sk_callback_lock,
            af_callback_keys + sk->sk_family,
            af_family_clock_key_strings[sk->sk_family]);

    sk->sk_state_change = sock_def_wakeup;
    sk->sk_data_ready = sock_def_readable;
    sk->sk_write_space = sock_def_write_space;
    sk->sk_error_report = sock_def_error_report;
    sk->sk_destruct = sock_def_destruct;

    sk->sk_sndmsg_page = NULL;
    sk->sk_sndmsg_off = 0;

    sk->sk_peercred.pid = 0;
    sk->sk_peercred.uid = -1;
    sk->sk_peercred.gid = -1;
    sk->sk_write_pending = 0;
    sk->sk_rcvlowat = 1;
    sk->sk_rcvtimeo = MAX_SCHEDULE_TIMEOUT;
    sk->sk_sndtimeo = MAX_SCHEDULE_TIMEOUT;

    sk->sk_stamp = ktime_set(-1L, 0);

    atomic_set(&sk->sk_refcnt, 1);
    atomic_set(&sk->sk_drops, 0);
}

sock结构中设置了几个头,它是sk_buff_head结构,我们在前边看sock结构中还没有这个结构体

struct sk_buff_head {
    /* These two members must be first. */
    struct sk_buff *next;
    struct sk_buff *prev;

    __u32 qlen;
    spinlock_t lock;
};

这个结构内部包含了sk_buff结构,它的内容非常大,我们就不看了sk_buff结构是一个非常重要的数据结构体,无论何种协议都是用该结构体用于封装、载运数据包来使用,每一个数据包都要用一个sk_buff数据结构,所以我们可以看到上面为sk_receive_queue是已经接收到的包,而sk_write_queue则为要发送的的包,上面函数中所有的赋值操作我们暂且不详细分析了,这里是钩子挂钩的过程,当我们遇到具体的操作时,可以回来查看这里的钩子上到底挂了哪个函数。接着,inet_create()还会对inet_sock结构也进行了一些初始化设置,我们还是到“用时理解”其作用,函数中最关键的地方

    if (sk->sk_prot->init) {
        err = sk->sk_prot->init(sk);/* wumingxiaozu */

这个就是调用了我们上面提到的运输层结构中的钩子函数init.我们看到上面sk_alloc()函数中将是将运输层的钩子结构tcp_prot挂入到sk_prot上了,不明白朋友请回到sk_alloc()函数那里看一下是如何挂入的。这里理所当然进入其init函数这个钩子函数中

struct proto tcp_prot = {
    .name            = "TCP",
    .owner            = THIS_MODULE,
    .close            = tcp_close,
    .connect        = tcp_v4_connect,
    .disconnect        = tcp_disconnect,
    .accept            = inet_csk_accept,
    .ioctl            = tcp_ioctl,
    .init            = tcp_v4_init_sock,
    .destroy        = tcp_v4_destroy_sock,
    .shutdown        = tcp_shutdown,
    .setsockopt        = tcp_setsockopt,
    .getsockopt        = tcp_getsockopt,
    .recvmsg        = tcp_recvmsg,
    .backlog_rcv        = tcp_v4_do_rcv,
    .hash            = inet_hash,
    .unhash            = inet_unhash,
    .get_port        = inet_csk_get_port,
    .enter_memory_pressure    = tcp_enter_memory_pressure,
    .sockets_allocated    = &tcp_sockets_allocated,
    .orphan_count        = &tcp_orphan_count,
    .memory_allocated    = &tcp_memory_allocated,
    .memory_pressure    = &tcp_memory_pressure,
    .sysctl_mem        = sysctl_tcp_mem,
    .sysctl_wmem        = sysctl_tcp_wmem,
    .sysctl_rmem        = sysctl_tcp_rmem,
    .max_header        = MAX_TCP_HEADER,
    .obj_size        = sizeof(struct tcp_sock),
    .twsk_prot        = &tcp_timewait_sock_ops,
    .rsk_prot        = &tcp_request_sock_ops,
    .h.hashinfo        = &tcp_hashinfo,
#ifdef CONFIG_COMPAT
    .compat_setsockopt    = compat_tcp_setsockopt,
    .compat_getsockopt    = compat_tcp_getsockopt,
#endif
};

我是无名小卒,转载请注明出处,不要担心结构有多么大,很多朋友在看代码时总是被结构所吓倒,要知道一个好的结构体也不是一天所成更不是一人完成的,所以我们要针对场景来分析记忆(用时理解)。其实上面我们就只关心一个地方.init = tcp_v4_init_sock,,进入钩子函数

 

阅读(2115) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~