内核网络子系统之 ----socket系统调用 篇
来源:互联网 发布:ug数控车编程视频教程 编辑:程序博客网 时间:2024/06/08 11:05
一切操作网络的应用都是通过socket,所以我们从最上层的BSD socket开始。以socket函数accpet为例。
Linux系统使用0x80软中断支持系统调用,同样socket也是使用这个中断从用户态进入到内核态。一般而言,对于每个系统调用都有一个内核如何函数与之对应,比如代开文件open,对应的系统调用入口函数是sys_open,同理,read对应sys_read,write对应sys_write。但是,socket的系统调用可不是这样的,它有一个统一的内核入口函数sys_socketcall。
贴段代码碎片:
switch (call) {
case 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;
... ...
}
明白了吧,实际上里面还是有条件判断的,最终还是调用对应的sys_xxx了。但无论如何,内核入口函数就一个,确实是节省了不少系统调用号。那应用层调用了accept,它是如何找到sys_socketcall这个函数的呢?call这个又是怎么被传进去的呢?下面我们一一解决。
首先看看,当应用层调用accept时,是如何进入到内核入口函数sys_socketcall的。
第一层入口:accepts.S(glibc里面的socket实现) /glib/sysdeps/unix/sysv/linux/accept.S
当应用层调用accept时,它会先跑到这里面来,下面看看它的部分源码:
#define socket accept
#define NARGS 3 //表明accept系统调用的参数个数
#define _socket _lib_accept
#include <socket.S> //socket的通用实现
下面看个其他的,listen.S
#define socket listen
#define NARGS 3 //表明accept系统调用的参数个数
#define NO_WEAK_ALIAS 1 //表明没有别名
#include <socket.S> //socket的通用实现
从上面可以看出来,每个调用都有一个.S文件,都被定义成socket,都用同一个socket.S处理
第二层入口:socket.S/glib/sysdeps/unix/sysv/linux/i386/socket.S
/*这个要在上面定义其它函数中使用,如accept,bind等,在上面的具体函数中有些定义了_socket,有些没有,有些定义了NO_WEAK_ALIAS。这些都是为选择不同的函数做设置的*/
#include <sysdep.h>
#include <socketcall.h>
#define P(a, b) P2(a, b)
#define P2(a, b) a##b //##表示连接符,将两个符号连接起来,比如ab
#ifndef _socket
# ifndef NO_WEAK_ALIAS
# define _socket P(_,socket)
# else
# define _socket socket
# endif
#endif
.globl _socket
ENTRY (_socket)
/* Saveregisters. */
movl %ebx,%edx
movl $SYS_ify(socketcall), %eax /*System call number in %eax. */
/* Use ##so `socket' is a separate token that might be #define'd. */
/* 这个号是来区别调用哪个具体函数的,是socket还是bind
这里的socket的具体值是会发生变化的,就是在上面的#define socket bind
这样类似的语句中变换这个socket的值,关于其具体的值在后面给出
*/
movl $P(SOCKOP_, socket), %ebx
lea 4(%esp),%ecx /* Address of args is 2nd arg. 这里以堆栈方式传递其它的参数 */
/* softinterrupt */
int 0x80
/* Restoreregisters. 恢复寄存器 */
movl %edx,%ebx
/* %eax is< 0 if there was an error. 比较返回值 */
cmpl $-125,%eax
jae SYSCALL_ERROR_LABEL
/*Successful; return the syscall's value. 正常返回 */
L(pseudo_end):
ret
上面两个主要的宏定义如下:
glib/sysdeps/unix/sysdep.h
#define SYS_ify(syscall_name) SYS_##syscall_name
所以,下面这行
movl $SYS_ify(socketcall), %eax /* System call number in%eax. */
翻译过来就是
movl $SYS_socketcall %eax
在调用int $0x80软中断从用户态到内核态是,eax里面保存的是本次调用的系统调用号。那么究竟这个值是多少呢,下面给出了答案:
/include/linux/unistd.h
#define _NR_socketcall 102
/usr/include/asm/bits/syscall.h
#define SYS_socketcall _NR_socketcall
至此,我们知道了socket调用是如何找到对应的系统调用号的了。下面我们还要关注上面提出的问题,如何知道是accept还是bind呢,因为我们此时只是找到了一个统一的socket内核入口sys_socketcall,如何将call参数传进去呢?答案在这里
movl $P(SOCKOP_, socket), %ebx
因为我们在accept.S里面,将accept定义成socket,所以在这里的命令翻译过来就是:
movl $SOCKOP_accept, %ebx
这里,ebx存储的是sys_socketcall的第一个参数。
然后,从/sysdeps/unix/sysv/linux/socketcall.h里面,我们将得知
#define SOCKOP_listen 4
#define SOCKOP_accept 5
#define SOCKOP_getsockname 6
我们在sys_socketcall的实现里面,对应的第一个参数call,用来表示的均以SYS_开始,比如SYS_ACCEPT,这个与上面的SOCKOP_accept也不一致啊,这时我们对应一下/include/linux/net.h文件,就会发现
#define SYS_LISTEN 4 /*sys_listen(2) */
#define SYS_ACCEPT 5 /* sys_accept (2) */
#define SYS_GETSOCKNAME 6 /* sys_getsockname(2) */
至此,我们也知道sys_socketcall里面的条件call是如何传进去的了。
细心的朋友可能会意识到,上面只提到了一个SYS_socketcall的系统调用号102,根据这个值,是怎么最终call到sys_socketcall这个函数的呢?下面继续解决这个问题,
第三层入口: entry.S /linux/arch/i386/kernel/entry.S
cmpl $(nr_syscalls), %eax
jae syscall_badsys
call *sys_call_table(,%eax,4)
nr_syscalls与系统中断号比较,如果相等,则在sys_call_table里面寻找对应的函数,该系统调用号和函数对应表的定义如下
sys_call_table:
.long _syssocket /* 102 */
至此,我们明白了,accept系统调用,先找到对应的accept.S,并在通用的socket.S里面找到accept的系统调用号SYS_socketcall,并将这个系统调用号保存在eax中。
然后根据accept.S里面的socket定义,查出当前是accept调用SOCKOP_accept,并将其作为第一个参数保存在ebx中,接下来将第二个参数保存在ecx中。
最后在entry.S里面,通过系统调用号SYS_socketcall,在sys_call_table里面找到了最终的调用函数sys_socketcall,而sys_socketcall根据socket.S中设置的第一个参数SOCKOP_accpet,也就是call == SYS_ACCEPT,成功的找到了sys_accept函数。
总之,虽然看起来复杂一点,实际上还是比较简单的。
- 内核网络子系统之 ----socket系统调用 篇
- linux内核网络子系统
- linux内核网络栈---socket调用内核路径
- Linux内核之系统调用
- Linux内核之系统调用
- linux内核之系统调用
- linux内核之系统调用
- Linux内核之网络系统
- 内核网络子系统的实现
- Linux网络子系统内核分析
- linux:socket 系统调用在linux内核中的实现流程图
- Linux内核学习之系统调用
- linux内核分析之系统调用
- linux内核介绍之系统调用过程
- Linux 内核开发之系统调用
- LINUX内核设计思想之系统调用
- linux 内核学习之系统调用
- Linux内核学习之系统调用
- Javascript操作滚动条
- SQL 语句练习
- mutableCopy与COPY区别
- Struts2学习笔记
- 堆:左倾树
- 内核网络子系统之 ----socket系统调用 篇
- swt中text只能输入数字的完美解决
- python读excel示例
- Fedora 键盘鼠标输入没反应及解决办法
- CSS学习总结1
- 数据集市与数据仓库个人看法
- C++ 面试题
- [推荐]ORACLE SQL:经典查询练手第五篇(不懂装懂,永世饭桶!)
- 物化视图详解--介绍、创建方法、例子