Linux内核网络栈1.2.13-有关tcp/ip协议的基础入门

参考资料
<<linux内核网络栈源代码情景分析>>
Linux内核网络栈的基础内容

主要分析tcp/ip相关的基本构成,概述了socket的系统调用进入内核的一个流程,并了解了协议的执行流程。为后续的理解学习做铺垫。

应用程序调用进入内核的过程

Tcp/ip协议示意图如下;

在这里插入图片描述

在Linux操作系统上面,从用户态进入内核态的方式就是通过系统调用INT $0x80进入内核执行函数,中断处理函数通过不同的系统调用号来选择操作系统初始化时注册的函数,对于socket,bind,connect等函数都是通过该方式来调用的,这些方法就是用户态调用的相关方法,因为linux在启动初始化的过程中会调用trap_init函数注册相关的事件的回调函数,其中就会执行set_system_gate(0x80,&system_call);函数,该函数就是注册调用号为0x80的函数,对应的回调函数为system_call,system_call函数就是汇编函数实现的位于i386/kernel/entry.S中;

.align 4
_system_call:
	pushl %eax			# save orig_eax
	SAVE_ALL
	movl $-ENOSYS,EAX(%esp)
	cmpl $(NR_syscalls),%eax
	jae ret_from_sys_call
	movl _sys_call_table(,%eax,4),%eax
	testl %eax,%eax
	je ret_from_sys_call
	movl _current,%ebx
	andl $~CF_MASK,EFLAGS(%esp)	# clear carry - assume no errors
	movl $0,errno(%ebx)
	movl %db6,%edx
	movl %edx,dbgreg6(%ebx)  # save current hardware debugging status
	testb $0x20,flags(%ebx)		# PF_TRACESYS
	jne 1f
	call *%eax
	movl %eax,EAX(%esp)		# save the return value
	movl errno(%ebx),%edx
	negl %edx
	je ret_from_sys_call
	movl %edx,EAX(%esp)
	orl $(CF_MASK),EFLAGS(%esp)	# set carry to indicate error
	jmp ret_from_sys_call

从这段汇编代码可知,根据传入的系统调用号会在_sys_call_table中去查找对应位置的系统函数,对应的_sys_call_table列表的初始化位于sparc/kernel/entry.S中,

		.align 4
		.globl C_LABEL(sys_call_table)
C_LABEL(sys_call_table):
	.long C_LABEL(sys_setup)		/* 0 */
	.long C_LABEL(sys_exit)
	.long C_LABEL(sys_fork)
	.long C_LABEL(sys_read)
	.long C_LABEL(sys_write)
	.long C_LABEL(sys_open)			/* 5 */
	.long C_LABEL(sys_close)
	.long C_LABEL(sys_waitpid)
	
	...
	
	.long C_LABEL(sys_munmap)
	.long C_LABEL(sys_truncate)
	.long C_LABEL(sys_ftruncate)
	.long C_LABEL(sys_fchmod)
	.long C_LABEL(sys_fchown)		/* 95 */
	.long C_LABEL(sys_getpriority)
	.long C_LABEL(sys_setpriority)
	.long C_LABEL(sys_profil)
	.long C_LABEL(sys_statfs)
	.long C_LABEL(sys_fstatfs)		/* 100 */
	.long C_LABEL(sys_ni_syscall)
	.long C_LABEL(sys_socketcall) 				// socket相关系统调用处理函数
	.long C_LABEL(sys_syslog)
	.long C_LABEL(sys_setitimer)
	.long C_LABEL(sys_getitimer)		/* 105 */
	
	...
	
	.align 4

此时查看指向的函数sys_socketcall函数内容;

asmlinkage int sys_socketcall(int call, unsigned long *args)
{
	int er;
	switch(call) 
	{
		case SYS_SOCKET:
			er=verify_area(VERIFY_READ, args, 3 * sizeof(long));  			// 检查输入参数的合法性
			if(er)
				return er; 													// 调用socket初始化执行
			return(sock_socket(get_fs_long(args+0),
				get_fs_long(args+1),
				get_fs_long(args+2)));
		case SYS_BIND:
			er=verify_area(VERIFY_READ, args, 3 * sizeof(long)); 		   
			if(er)
				return er;
			return(sock_bind(get_fs_long(args+0),
				(struct sockaddr *)get_fs_long(args+1),
				get_fs_long(args+2))); 										// 套接字绑定初始化
		case SYS_CONNECT:
			er=verify_area(VERIFY_READ, args, 3 * sizeof(long));
			if(er)
				return er;
			return(sock_connect(get_fs_long(args+0),
				(struct sockaddr *)get_fs_long(args+1),
				get_fs_long(args+2))); 									   // 套接字连接
		case SYS_LISTEN:
			er=verify_area(VERIFY_READ, args, 2 * sizeof(long));
			if(er)
				return er;
			return(sock_listen(get_fs_long(args+0),
				get_fs_long(args+1))); 									  // 套接字监听
		case SYS_ACCEPT:
			er=verify_area(VERIFY_READ, args, 3 * sizeof(long));
			if(er)
				return er;
			return(sock_accept(get_fs_long(args+0),
				(struct sockaddr *)get_fs_long(args+1),
				(int *)get_fs_long(args+2))); 							  // 接受新请求
		case SYS_GETSOCKNAME:
			er=verify_area(VERIFY_READ, args, 3 * sizeof(long));
			if(er)
				return er;
			return(sock_getsockname(get_fs_long(args+0),
				(struct sockaddr *)get_fs_long(args+1),
				(int *)get_fs_long(args+2))); 							 // 获取host信息
		case SYS_GETPEERNAME:
			er=verify_area(VERIFY_READ, args, 3 * sizeof(long));
			if(er)
				return er;
			return(sock_getpeername(get_fs_long(args+0),
				(struct sockaddr *)get_fs_long(args+1),
				(int *)get_fs_long(args+2)));
		case SYS_SOCKETPAIR:
			er=verify_area(VERIFY_READ, args, 4 * sizeof(long));
			if(er)
				return er;
			return(sock_socketpair(get_fs_long(args+0),
				get_fs_long(args+1),
				get_fs_long(args+2),
				(unsigned long *)get_fs_long(args+3))); 				
		case SYS_SEND:
			er=verify_area(VERIFY_READ, args, 4 * sizeof(unsigned long));
			if(er)
				return er;
			return(sock_send(get_fs_long(args+0),
				(void *)get_fs_long(args+1),
				get_fs_long(args+2),
				get_fs_long(args+3))); 									// 发送信息
		case SYS_SENDTO:
			er=verify_area(VERIFY_READ, args, 6 * sizeof(unsigned long));
			if(er)
				return er;
			return(sock_sendto(get_fs_long(args+0),
				(void *)get_fs_long(args+1),
				get_fs_long(args+2),
				get_fs_long(args+3),
				(struct sockaddr *)get_fs_long(args+4),
				get_fs_long(args+5)));
		case SYS_RECV:
			er=verify_area(VERIFY_READ, args, 4 * sizeof(unsigned long));
			if(er)
				return er;
			return(sock_recv(get_fs_long(args+0),
				(void *)get_fs_long(args+1),
				get_fs_long(args+2),
				get_fs_long(args+3))); 									// 接受信息
		case SYS_RECVFROM:
			er=verify_area(VERIFY_READ, args, 6 * sizeof(unsigned long));
			if(er)
				return er;
			return(sock_recvfrom(get_fs_long(args+0),
				(void *)get_fs_long(args+1),
				get_fs_long(args+2),
				get_fs_long(args+3),
				(struct sockaddr *)get_fs_long(args+4),
				(int *)get_fs_long(args+5)));
		case SYS_SHUTDOWN:
			er=verify_area(VERIFY_READ, args, 2* sizeof(unsigned long));
			if(er)
				return er;
			return(sock_shutdown(get_fs_long(args+0),
				get_fs_long(args+1)));
		case SYS_SETSOCKOPT:
			er=verify_area(VERIFY_READ, args, 5*sizeof(unsigned long));
			if(er)
				return er;
			return(sock_setsockopt(get_fs_long(args+0),
				get_fs_long(args+1),
				get_fs_long(args+2),
				(char *)get_fs_long(args+3),
				get_fs_long(args+4))); 									// 设置套接字配置信息
		case SYS_GETSOCKOPT:
			er=verify_area(VERIFY_READ, args, 5*sizeof(unsigned long));
			if(er)
				return er;
			return(sock_getsockopt(get_fs_long(args+0),
				get_fs_long(args+1),
				get_fs_long(args+2),
				(char *)get_fs_long(args+3),
				(int *)get_fs_long(args+4))); 						   // 获取套接字配置
		default:
			return(-EINVAL);
	}
}

应用程序调用socket的相关的流程就基本完成。

基于TCP/IP的结构图

在这里插入图片描述

整个的基本结构流程图如上所述。在socket.c文件中主要是做了相关检查之后就调用了inet层相关的函数,主要位于af_inet.c文件中,根据不同的协议在继续调用协议层对应的处理函数,然后再处理过ip层相关的信息,再调用相关的驱动的接口程序,讲数据发送。

总结

本文内容整理出于<<linux内核网络栈源代码情景分析>>,主要先概述了解了基本的协议的构成,从用户态到内核态的执行过程,本文只是简单的入门了解,后续内容会继续学习。由于本人才疏学浅,如有错误请批评指正。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值