Socket与系统调用深度分析

Posted gfsh

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Socket与系统调用深度分析相关的知识,希望对你有一定的参考价值。

废话不多说,直接动手实战。

本次实验追踪有关Socket的系统调用中非常重要的两个bind()和listen(),步骤如下:

1.修改menu中的Makefile文件。

  我的主目录为MenuOS,该目录下的文件如下:

技术图片

cd ~/MenuOS/menu   #进入menu文件夹
sudo su            # 切换至root用户以修改Makefile文件
gedit Makefile

接着做如下红框中的修改:

技术图片

 

 接着,仍然在menu文件夹下,执行

make rootfs

 此时切不可关闭该终端和QEMU,打开另一个终端,输入如下命令:

gdb
file ~/MenuOS/linux-5.0.1/vmlinux
target remote:1234
break __sys_bind
break __sys_listen

此时,gdb已经给__sys_bind和__sys_listen两个Socket系统调用设定了断点,gdb响应如下:

技术图片

 

  说明gdb已找到这两条系统调用的函数定义所在。

然后开始对MenuOS的运行:

c #在gdb终端输入,接下来两条命令在QEMU中输入
replyhi
hello

当我们在qemu中输入replyhi时,gdb终端显示停在了bind函数。

技术图片

 

 我们可以利用list命令来查看bind的源代码,但是list查看行数默认有限,所以:

set listsize 60     #设置一次list可查看60行代码
list        #查看bind函数源代码

技术图片

 

 

/*
 *    Bind a name to a socket. Nothing much to do here since it‘s
 *    the protocol‘s responsibility to handle the local address.
 *
 *    We move the socket address to kernel space before we call
 *    the protocol layer (having also checked the address is ok).
 */

/*
 *上面一段话的大概意思是,bind()系统调用仅负责将进程的名字与socket绑定;
 *此外,bind()也负责将该socket转入内核处理;
 *至于处理本地地址,这是网络协议所需要做的事情。
 */
int __sys_bind(int fd, struct sockaddr __user *umyaddr, int addrlen)
{
    struct socket *sock;
    struct sockaddr_storage address;
    int err, fput_needed;
        /*
         *以fd为索引从当前进程的文件描述符表中,找到对应的file实例,
         *然后从file实例的private_data中,获取socket实例
         */

    sock = sockfd_lookup_light(fd, &err, &fput_needed);
    if (sock) {
        /*
         * 将用户空间的地址拷贝到内核空间的缓冲区中
         */
        err = move_addr_to_kernel(umyaddr, addrlen, &address);
        if (!err) {
            /*
             * SELinux相关,不需要关心。
             */
            err = security_socket_bind(sock,
                           (struct sockaddr *)&address,
                           addrlen);
            /*
             * 如果是TCP套接字,sock->ops指向的是inet_stream_ops,
             * sock->ops是在inet_create()函数中初始化,所以bind接口
             * 调用的是inet_bind()函数。
             */

            if (!err)
                err = sock->ops->bind(sock,
                              (struct sockaddr *)
                              &address, addrlen);
        }
        fput_light(sock->file, fput_needed);
    }
    return err;
}

SYSCALL_DEFINE3(bind, int, fd, struct sockaddr __user *, umyaddr, int, addrlen)
{
    return __sys_bind(fd, umyaddr, addrlen);
}

接着在gdb终端下按c,程序继续执行,然后停在了listen函数。

技术图片

 

 同样的list一下,

技术图片

 

 

/*
 *    Perform a listen. Basically, we allow the protocol to do anything
 *    necessary for a listen, and if that works, we mark the socket as
 *    ready for listening.
 */

/*
 *上述解释的大致含义是,该系统调用的作用是将端口置为监听状态;
 *具体操作视协议而定
*/

int __sys_listen(int fd, int backlog)
{
    struct socket *sock;
    int err, fput_needed;
    int somaxconn;

    sock = sockfd_lookup_light(fd, &err, &fput_needed);
    if (sock) {
        /*
         * sysctl_somaxconn存储的是服务器监听时,允许每个套接字连接队列长度 
         * 的最大值,默认值是128
         */
        somaxconn = sock_net(sock->sk)->core.sysctl_somaxconn;
        /*
         * 如果指定的最大连接数超过系统限制,则使用系统当前允许的连接队列
         * 中连接的最大数。
         */
        if ((unsigned int)backlog > somaxconn)
            backlog = somaxconn;

        err = security_socket_listen(sock, backlog);
        if (!err)
        /*
         * 从这里开始,socket以后所用的函数将根据TCP/UDP而视协议而定
         */
            err = sock->ops->listen(sock, backlog);

        fput_light(sock->file, fput_needed);
    }
    return err;
}

SYSCALL_DEFINE2(listen, int, fd, int, backlog)
{
    return __sys_listen(fd, backlog);
}

以上是关于Socket与系统调用深度分析的主要内容,如果未能解决你的问题,请参考以下文章

Socket与系统调用深度分析

Socket与系统调用深度分析

Socket与系统调用深度分析

Socket系统调用Socket与系统调用深度分析

Socket与系统调用深度分析

Socket与系统调用深度分析