Chinaunix首页 | 论坛 | 博客
  • 博客访问: 59204
  • 博文数量: 8
  • 博客积分: 1588
  • 博客等级: 上尉
  • 技术积分: 145
  • 用 户 组: 普通用户
  • 注册时间: 2010-04-06 18:05
文章分类

全部博文(8)

文章存档

2010年(8)

我的朋友

分类: LINUX

2010-05-25 10:47:05

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) |
给主人留下些什么吧!~~