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

全部博文(71)

文章存档

2016年(4)

2015年(2)

2014年(2)

2013年(63)

分类: LINUX

2013-06-14 16:43:20

 
 

sys_socketcall()-->sys_socket()-->sock_create()-->__sock_create()-->通过pf->create()--> inet_create()-->通过sk->sk_prot->init()-->tcp_v4_init_sock()

static int tcp_v4_init_sock(struct sock *sk)
{
    struct inet_connection_sock *icsk = inet_csk(sk);
    struct tcp_sock *tp = tcp_sk(sk);

    skb_queue_head_init(&tp->out_of_order_queue);
    tcp_init_xmit_timers(sk);
    tcp_prequeue_init(tp);

    icsk->icsk_rto = TCP_TIMEOUT_INIT;
    tp->mdev = TCP_TIMEOUT_INIT;

    /* So many TCP implementations out there (incorrectly) count the
     * initial SYN frame in their delayed-ACK and congestion control
     * algorithms that we must have the following bandaid to talk
     * efficiently to them. -DaveM
     */
    tp->snd_cwnd = 2;

    /* See draft-stevens-tcpca-spec-01 for discussion of the
     * initialization of these values.
     */
    tp->snd_ssthresh = 0x7fffffff;    /* Infinity */
    tp->snd_cwnd_clamp = ~0;
    tp->mss_cache = 536;

    tp->reordering = sysctl_tcp_reordering;
    icsk->icsk_ca_ops = &tcp_init_congestion_ops;

    sk->sk_state = TCP_CLOSE;

    sk->sk_write_space = sk_stream_write_space;
    sock_set_flag(sk, SOCK_USE_WRITE_QUEUE);

    icsk->icsk_af_ops = &ipv4_specific;
    icsk->icsk_sync_mss = tcp_sync_mss;
#ifdef CONFIG_TCP_MD5SIG
    tp->af_specific = &tcp_sock_ipv4_specific;
#endif

    sk->sk_sndbuf = sysctl_tcp_wmem[1];
    sk->sk_rcvbuf = sysctl_tcp_rmem[1];

    atomic_inc(&tcp_sockets_allocated);

    return 0;
}

首先函数出现了一个新的结构体struct tcp_sock,这个结构非常大,其内容多与滑动窗口等发送、接收的信息相关,这些内容多是与tcp协议关系密切,其作用随着我们的分析会越来越清晰,上面代码中对这个结构变量tpsock结构变量sk进一步的初始化,对后这个地方的初始化工作我们要经常回顾,例如这里的sk->sk_write_space = sk_stream_write_space等钩子函数的挂入,以及缓冲区的相关设置。至于代码中出现的inet_connection_sock结构体我们在今后的章节中详细列出,它的主要是inet_sock的结构体的一个扩展,是一些关于连接操作的具体信息,这里都是对这些结构的初始化设置,涉及到以后的很多操作,还是用时分析吧,暂且在这里做一记号,以后肯定要往返查询这里的钩子函数和钩子结构,最后增加tcp_sockets_allocated全局的tcpsocket计数器。下图是创建过程中涉及到的数据结构关系,从图中可以看出,从sock出发可以得到inet_sock的指针,进一步可以得到inet_connection_sock的指针,最后得到tcp_sock的指针,这是因为前一个结构体在后一个结构体的最前端,即sock起始地址也是其他结构的起始地址,并且图中也画出了关于接收队列和发送队列与数据包sk_buff的关系。

 

完成了创建并初化socket工作后,最后我们回到上一节提到的sys_socket()函数中执行retval = sock_map_fd(sock)来完成创建过程

 

sys_socketcall()-->sys_socket()-->sock_map_fd()

int sock_map_fd(struct socket *sock)
{
    struct file *newfile;
    int fd = sock_alloc_fd(&newfile);

    if (likely(fd >= 0)) {
        int err = sock_attach_fd(sock, newfile);

        if (unlikely(err < 0)) {
            put_filp(newfile);
            put_unused_fd(fd);
            return err;
        }
        fd_install(fd, newfile);/* wumingxiaozu */
    }
    return fd;
}

这个函数内部调用了一个与文件系统有关联的操作函数

 

sys_socketcall()-->sys_socket()-->sock_map_fd()-->sock_alloc_fd()

static int sock_alloc_fd(struct file **filep)
{
    int fd;

    fd = get_unused_fd();
    if (likely(fd >= 0)) {
        struct file *file = get_empty_filp();

        *filep = file;
        if (unlikely(!file)) {
            put_unused_fd(fd);
            return -ENFILE;
        }
    } else
        *filep = NULL;
    return fd;
}

上面是分配一个文件指针的数据结构,并申请一个文件号与他挂钩,之后返回文件号。接着进入

 

sys_socketcall()-->sys_socket()-->sock_map_fd()-->sock_attach_fd()

static int sock_attach_fd(struct socket *sock, struct file *file)
{
    struct dentry *dentry;
    struct qstr name = { .name = "" };

    dentry = d_alloc(sock_mnt->mnt_sb->s_root, &name);
    if (unlikely(!dentry))
        return -ENOMEM;

    dentry->d_op = &sockfs_dentry_operations;
    
/*
     * We dont want to push this dentry into global dentry hash table.
     * We pretend dentry is already hashed, by unsetting DCACHE_UNHASHED
     * This permits a working /proc/$pid/fd/XXX on sockets qinjiana0786
     */

    dentry->d_flags &= ~DCACHE_UNHASHED;
    d_instantiate(dentry, SOCK_INODE(sock));

    sock->file = file;
    init_file(file, sock_mnt, dentry, FMODE_READ | FMODE_WRITE,
         &socket_file_ops);
    SOCK_INODE(sock)->i_fop = &socket_file_ops;
    file->f_flags = O_RDWR;
    file->f_pos = 0;
    file->private_data = sock;

    return 0;
}

在这个函数中我们看到在socket文件系统中分配了一个目录项,这些是关于文件系统的概念,朋友们可以参阅相关资料,然后将socket与文件指针挂上关系,不过我们在上面看到对目录项的操作函数挂钩的是

dentry->d_op = &sockfs_dentry_operations;
static struct dentry_operations sockfs_dentry_operations = {
    .d_delete = sockfs_delete_dentry,
    .d_dname = sockfs_dname,
};

这里的主要操作是删除目录项。最关键的地方是上边对文件操作指针的挂钩

static const struct file_operations socket_file_ops = {
    .owner = THIS_MODULE,
    .llseek = no_llseek,
    .aio_read = sock_aio_read,
    .aio_write = sock_aio_write,
    .poll = sock_poll,
    .unlocked_ioctl = sock_ioctl,
#ifdef CONFIG_COMPAT
    .compat_ioctl = compat_sock_ioctl,
#endif
    .mmap = sock_mmap,
    .open = sock_no_open, /* special open code to disallow open via /proc */
    .release = sock_close,
    .fasync = sock_fasync,
    .sendpage = sock_sendpage,
    .splice_write = generic_splice_sendpage,
    .splice_read = sock_splice_read,
};

这个挂钩可以使我们在能过open(),read()write()象操作文件那样对socket进行读写操作,上边代码最重要的是将file结构中的private_data指向了我们服务器端的socket,以后我们会看到要通过file指针再从private_data中重新取出这个socket指针,而另一侧在系统调用的角度,我们可以继续能过sys_socketcall()系统调用对socket进行sendrecv操作,从这里我们可以看出socket是与文件系统紧密相关的,这也就印证了之前我说的那句话,内核是一个整体,尽管很多朋友觉得自己只要学某一部分就行了,其实,你越学越深时会发现单凭某一方面的知识是远远不足的,这也是很多人都认为学习内核的困难所在,而解决的最有效的方法就是需要我们不断的提高自己阅读代码的能力更需要对内核全面学习。

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