linux内核网络协议分析1-----socket创建

OS: redhat linux 5.0;

内核版本: 2.6.18;


之前在写socket 程序时都是简单的调用 socket 函数,不明白这个函数都会调用哪些程序,内核又是如何执行程序的,最近自己也在看 linux内核关于socket部分的程序,大致明白了一些过程,现在写上我对socket创建过程的理解,由于篇幅较大所以会不定时更新,如果自己博客中的东西有什么不对的请指出来,我现在仍然是在一边学习一边记录的过程,所以如果有什么不对的请多多指教,谢谢。

特别感谢 linux 系统内核的无私开源!!!

我们知道在写socket 程序时 socket 程序是这么调用的,


socketfd = socket(AF_INET, SOCK_STREAM, 0);

创建完socket 就可以监听和手法数据了,至于socket 程序时如何调用系统函数,请耐心看后面的文章···

socket函数会调用系统函数 sys_socketcall()

 *	System call vectors. 
 *
 *	Argument checking cleaned up. Saved 20% in size.
 *  This function doesn't need to set the kernel lock because
 *  it is set by the callees. 
 */

asmlinkage long sys_socketcall(int call, unsigned long __user *args)
{
	unsigned long a[6];
	unsigned long a0,a1;
	int err;

	if(call<1||call>SYS_RECVMSG)
		return -EINVAL;

	/* copy_from_user should be SMP safe. */
	if (copy_from_user(a, args, nargs[call]))
		return -EFAULT;

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

	a0=a[0];
	a1=a[1];
	
	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;
		case SYS_ACCEPT:
			err = sys_accept(a0,(struct sockaddr __user *)a1, (int __user *)a[2]);
			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_send(a0, (void __user *)a1, a[2], a[3]);
			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_recv(a0, (void __user *)a1, a[2], a[3]);
			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 msghdr __user *) a1, a[2]);
			break;
		case SYS_RECVMSG:
			err = sys_recvmsg(a0, (struct msghdr __user *) a1, a[2]);
			break;
		default:
			err = -EINVAL;
			break;
	}
	return err;
}

参数call是socket的调用号,从linux头文件中我们可以看到 call 对应值的意思

#define SYS_SOCKET	1		/* sys_socket(2)		*/
#define SYS_BIND	2		/* sys_bind(2)			*/
#define SYS_CONNECT	3		/* sys_connect(2)		*/
#define SYS_LISTEN	4		/* sys_listen(2)		*/
#define SYS_ACCEPT	5		/* sys_accept(2)		*/
#define SYS_GETSOCKNAME	6		/* sys_getsockname(2)		*/
#define SYS_GETPEERNAME	7		/* sys_getpeername(2)		*/
#define SYS_SOCKETPAIR	8		/* sys_socketpair(2)		*/
#define SYS_SEND	9		/* sys_send(2)			*/
#define SYS_RECV	10		/* sys_recv(2)			*/
#define SYS_SENDTO	11		/* sys_sendto(2)		*/
#define SYS_RECVFROM	12		/* sys_recvfrom(2)		*/
#define SYS_SHUTDOWN	13		/* sys_shutdown(2)		*/
#define SYS_SETSOCKOPT	14		/* sys_setsockopt(2)		*/
#define SYS_GETSOCKOPT	15		/* sys_getsockopt(2)		*/
#define SYS_SENDMSG	16		/* sys_sendmsg(2)		*/
#define SYS_RECVMSG	17		/* sys_recvmsg(2)		*/

由于我们的程序时创建socket 所以sys_socketcall的参数call 值为1 即SYS_SOCKET,

扫描二维码关注公众号,回复: 5080036 查看本文章

之后

看一下程序中 copy_from_user这个函数,由于linux在内存的访问过程中分为内核态和用户态 这个程序的作用就是将用户态的参数拷贝到内核态来,其参数为nargs,其为需要从内核态复制到用户态的参数,再来看一下nargs的定义。

#define AL(x) ((x) * sizeof(unsigned long))
static unsigned char nargs[18]={AL(0),AL(3),AL(3),AL(3),AL(2),AL(3),
				AL(3),AL(3),AL(4),AL(4),AL(4),AL(6),
				AL(6),AL(2),AL(5),AL(5),AL(3),AL(3)};

可以看到当call为1时 nargs[1] = AL(3)。

转换一下得到 copy_from_user函数的变形为

if (copy_from_user(a, args, AL(3)))
其中 a是内核的空间, 而 args是参数在用户空间的地址, AL(3) 代表是3个字节的长度,copy_from_user的原型为

unsigned long
copy_from_user(void *to, const void __user *from, unsigned long n)
{
	BUG_ON((long) n < 0);
	if (access_ok(VERIFY_READ, from, n))
		n = __copy_from_user(to, from, n);
	else
		memset(to, 0, n);
	return n;
}
其函数在执行成功时会返回0,失败返回没有被拷贝的字节数,这个函数的详细分析就先不说了, 涉及到很多细节,我现在也没搞清楚呢, 现代过,以后有时间再好好研究研究总之这个函数就是将socket的三个参数从用户空间复制到内核空间。


之后通过switch 来确定程序的走向~~

由于call为1 所以会走到程序的第一个分支 执行sys_socket函数流程为 sys_socketcall->sys_socket,

asmlinkage long sys_socket(int family, int type, int protocol)
{
	int retval;
	struct socket *sock;

	retval = sock_create(family, type, protocol, &sock);
	if (retval < 0)
		goto out;

	retval = sock_map_fd(sock);
	if (retval < 0)
		goto out_release;

out:
	/* It may be already another descriptor 8) Not kernel problem. */
	return retval;

out_release:
	sock_release(sock);
	return retval;
}
此刻程序进入到了比较重要的地方, socket_create这个函数,







猜你喜欢

转载自blog.csdn.net/qianniu2meiyi/article/details/38063039