Fork me on GitHub

Coding Poineer

Coding Poineer

Coding Poineer

Coding Poineer

Coding Poineer

Coding Poineer

Coding Poineer

Coding Poineer

Coding Poineer

Coding Poineer

Coding Poineer

Socket与系统调用深度分析

Socket与系统调用深度分析

一、实验内容
研究menuos中replyhi、hello函数并通过gdb调试跟踪发现socket编程api与系统调用之间的关系

二、实验原理
socket调用流程:
(1)系统调用 –> (2)查找socket –> (3)执行socket的对应操作函数 –> (4)执行传输层协议的对应操作函数

linux系统调用流程:

如上图,系统调用执行的流程如下:

  1. 应用程序 代码调用系统调用( xyz ),该函数是一个包装系统调用的 库函数 ;
  2. 库函数 ( xyz )负责准备向内核传递的参数,并触发 软中断 以切换到内核;
  3. CPU 被 软中断 打断后,执行 中断处理函数 ,即 系统调用处理函数 ( system_call);
  4. 系统调用处理函数 调用 系统调用服务例程 ( sys_xyz ),真正开始处理该系统调用;

三、实验过程
修改menuos目录下的Makefile文件

	qemu-system-x86_64 -kernel ../linux-5.0.1/arch/x86_64/boot/bzImage -initrd ../rootfs.img -S -s -append nokaslr

打开gdb,打上断点,连接远程服务器,打开MenuOS,执行hello/hi程序

'''

SYSCALL_DEFINE2(socketcall, int, call, unsigned long __user *, args)
{
unsigned long a[AUDITSC_ARGS];
unsigned long a0, a1;
int err;
unsigned int len;

if (call < 1 || call > SYS_SENDMMSG)
    return -EINVAL;
call = array_index_nospec(call, SYS_SENDMMSG + 1);

len = nargs[call];
if (len > sizeof(a))
    return -EINVAL;

/* copy_from_user should be SMP safe. */
if (copy_from_user(a, args, len))
    return -EFAULT;

err = audit_socketcall(nargs[call] / sizeof(unsigned long), a);
if (err)
    return err;

a0 = a[0];
a1 = a[1];

switch (call) {            //根据call的不同之,从而执行不同的系统系统调用 当前call = 1 也就是执行
case SYS_SOCKET:                        // 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;
case SYS_GETSOCKNAME:
    err =
        __sys_getsockname(a0, (struct sockaddr __user *)a1,
                  (int __user *)a[2]);
    break;
case SYS_GETPEERNAME:
    err =
        __sys_getpeername(a0, (struct sockaddr __user *)a1,
                  (int __user *)a[2]);
    break;
case SYS_SOCKETPAIR:
    err = __sys_socketpair(a0, a1, a[2], (int __user *)a[3]);
    break;
case SYS_SEND:
    err = __sys_sendto(a0, (void __user *)a1, a[2], a[3],
               NULL, 0);
    break;
case SYS_SENDTO:
    err = __sys_sendto(a0, (void __user *)a1, a[2], a[3],
               (struct sockaddr __user *)a[4], a[5]);
    break;
case SYS_RECV:
    err = __sys_recvfrom(a0, (void __user *)a1, a[2], a[3],
                 NULL, NULL);
    break;
case SYS_RECVFROM:
    err = __sys_recvfrom(a0, (void __user *)a1, a[2], a[3],
                 (struct sockaddr __user *)a[4],
                 (int __user *)a[5]);
    break;
case SYS_SHUTDOWN:
    err = __sys_shutdown(a0, a1);
    break;
case SYS_SETSOCKOPT:
    err = __sys_setsockopt(a0, a1, a[2], (char __user *)a[3],
                   a[4]);
    break;
case SYS_GETSOCKOPT:
    err =
        __sys_getsockopt(a0, a1, a[2], (char __user *)a[3],
                 (int __user *)a[4]);
    break;
case SYS_SENDMSG:
    err = __sys_sendmsg(a0, (struct user_msghdr __user *)a1,
                a[2], true);
    break;
case SYS_SENDMMSG:
    err = __sys_sendmmsg(a0, (struct mmsghdr __user *)a1, a[2],
                 a[3], true);
    break;
case SYS_RECVMSG:
    err = __sys_recvmsg(a0, (struct user_msghdr __user *)a1,
                a[2], true);
    break;
case SYS_RECVMMSG:
    if (IS_ENABLED(CONFIG_64BIT) || !IS_ENABLED(CONFIG_64BIT_TIME))
        err = __sys_recvmmsg(a0, (struct mmsghdr __user *)a1,
                     a[2], a[3],
                     (struct __kernel_timespec __user *)a[4],
                     NULL);
    else
        err = __sys_recvmmsg(a0, (struct mmsghdr __user *)a1,
                     a[2], a[3], NULL,
                     (struct old_timespec32 __user *)a[4]);
    break;
case SYS_ACCEPT4:
    err = __sys_accept4(a0, (struct sockaddr __user *)a1,
                (int __user *)a[2], a[3]);
    break;
default:
    err = -EINVAL;
    break;
}
return err;
}

'''

可以看出,函数有一个参数call,在函数内部会根据call的不同值来确定返回不同的处理方式,在socket中有不同的函数如Socket():创建套接字,Bind():指定本地地址,Connect():将套接字连接到目的地址等,根据传入参数的不同进行相应的处理。
增加断点继续执行,如下图所示:

结果

实验结果分析:call的值依次为1,2,1,3,10,9,9,10,5,分别对应不同的网络系统调用,由此可以总结出下表:

posted @ 2019-12-19 19:46  365/24/60  阅读(182)  评论(0编辑  收藏  举报