Socket与系统调用深度分析

时间:2019-12-17
本文章向大家介绍Socket与系统调用深度分析,主要包括Socket与系统调用深度分析使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

一、Socket和系统调用

  操作系统是计算机资源的管理者,他保证资源被所有的进程共享,并且进程之间不会有干扰,为了达到这个目的,进程不会拥有操作硬件的功能,即进程在计算机上运行是受限制的。而操作系统为了对程序员提供操作硬件的功能,对外暴露一层接口,这层接口就称为系统调用(syscall)。系统调用有很多种类,今天我们主要讨论的是网络相关的系统调用。

 1 系统调用号 函数名  系统调用           所在文件
 2 41    socket    sys_socket    net/socket.c
 3 42    connect    sys_connect    net/socket.c
 4 43    accept    sys_accept    net/socket.c
 5 44    sendto    sys_sendto    net/socket.c
 6 45    recvfrom    sys_recvfrom    net/socket.c
 7 46    sendmsg    sys_sendmsg    net/socket.c
 8 47    recvmsg    sys_recvmsg    net/socket.c
 9 48    shutdown    sys_shutdown    net/socket.c
10 49    bind    sys_bind    net/socket.c
11 50    listen    sys_listen    net/socket.c
12 51    getsockname    sys_getsockname    net/socket.c
13 52    getpeername    sys_getpeername    net/socket.c
14 53    socketpair    sys_socketpair    net/socket.c
15 54    setsockopt    sys_setsockopt    net/socket.c
16 55    getsockopt    sys_getsockopt    net/socket.c

上述就是网络相关的主要的系统调用,可以看到我们的在编写网络程序的时候,socket、bind、listen、accept等都榜上有名。

但是在man手册上我看到这样一句话:

On a some architectures--for example, x86-64 and ARM--there is no socketcall() system call; instead socket(2), accept(2), bind(2), and so on really are implemented as separate system calls.

意味着在不同的体系结构上可能用的不是socket ---> sys_socket / bind ---> sys_bind 这样的调用方式,可能调用的是socketcall()。 下面我们来进行验证。

二、gdb跟踪内核代码

2.1 调用sys_socket? 

可以看到我们打断点在sys_socket函数上,并没有进入到断点,可以断定,创建socket的时候并没有调用sys_socket。

2.2 sys_socketcall?

果然在sys_socketcall打了断点后,运行我们的replyhi程序(命令?),即在断点处停止了,我们查看其函数调用栈,发现其进入系统调用的顺序是 entry_SYSENTER_32() ---> do_syscall_32_irqs_on()---->sys_socketcall()(实际上是调用的SYSCALL_DEFINE2函数)。我们来看看SYSCALL_DEFINE2()的代码:

 1 SYSCALL_DEFINE2(socketcall, int, call, unsigned long __user *, args)
 2 {
 3 switch (call) {
 4     case SYS_SOCKET:
 5         err = __sys_socket(a0, a1, a[2]);
 6         break;
 7     case SYS_BIND:
 8         err = __sys_bind(a0, (struct sockaddr __user *)a1, a[2]);
 9         break;
10     case SYS_CONNECT:
11         err = __sys_connect(a0, (struct sockaddr __user *)a1, a[2]);
12         break;
13     case SYS_LISTEN:
14         err = __sys_listen(a0, a1);
15         break;
16  ....
17 }

可以看到SYSCALL_DEFINE2()函数通过其参数call来决定执行哪个函数。说起来可能有点乱,下面我们来一张图:

图里很清晰的展示了其中的调用关系,在调用库函数bind、listen、accept等函数时,通过SYSCALL_DEFINE2中的type参数来分发调用具体的工作函数。如call=1,则调用__sys_socket()函数来创建套接字。

2.3 __sys_socket

下面我们就来具体的进入到完成工作的函数中去看看。我们这里以创建socket的函数为例。

可以看到我们进入了其中,并且其参数family和type的值分别为2和1。代表着使用的协议和类型。其他的也没什么好看,就是创建了一个socket套接字,并且返回了。而bind、listen和accept等函数的实现都是通过swtich的选择,跳转到对应的函数去执行的。

原文地址:https://www.cnblogs.com/luhaipeng/p/12055566.html