3-socket的实践到内核--追踪socket到内核
来源:互联网 发布:均衡器软件 编辑:程序博客网 时间:2024/06/05 07:20
socket(AF_UNIX, SOCK_STREAM, 0);
connect(sockfd, (struct sockaddr *)&address, len);
bind(server_sockfd, (struct sockaddr *)&server_address, server_len);
listen(server_sockfd, 5);
accept(server_sockfd,(struct sockaddr *)&client_address, client_len);
前面练习中我们用到上面这些函数,这些函数全部要通过sys_socketcall()系统调用,关于系统调用以前说过我们将会在关于中断的练习中详细论述,这里只要知道已经到达了系统调用函数处,这个函数在net/socket.c的2007行,注意本文针对的内核版本是2.6.26,我们再把内核代码贴在这里
/*
* 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;
}
这个函数是内核socket的总系统调用入口,参数call是具体的操作码,参数args是一个数组指针。另外我们需要明确从用户空间复制的参数数量,这是根据nargs[]来决定的,以call为下标将会从该数组中找到参数的个数,依据个数来把args处的参数从用户空间即我们的应用程序复制过来
/* Argument list sizes for sys_socketcall */
#define AL(x) ((x) * sizeof(unsigned long))
static const 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)
};
#undef AL
#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(AF_UNIX, SOCK_STREAM, 0);
socket(AF_INET, SOCK_STREAM, 0);
case SYS_SOCKET:
err = sys_socket(a0, a1, a[2]);
有朋友可能会被我贴的代码所吓倒,会问“这里只需要一个函数sys_socket()”那贴出全代码做什么,我们上面贴出sys_socketcall()的整个函数代码是为了让大家先对其有个整体的了解,今后我们只提到追踪到其中的一部分,即在此函数中的走向,所以希望用到时不了解的朋友可以查看这里的函数代码,这个假设是在你不想一边看这里的文章一边看你手中的源码时。这里应用程序界面函数会能过C函数库到达sys_socket(),我们继续追踪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;
}
这个函数从名称可以看出是为了修建一个插口,对照应用程序界面传递过来的三个参数,第一个参数family应用程序传递过来的值为AF_UNIX或者AF_INET,这里我们声明本节我们重要的针对第一种情况AF_UNIX展开论述。第二个参数是SOCK_STREAM,第三个参数是0。我们从上面的函数中可以看到是进入了sock_create()函数去创建socket。另外在2.6.26内核的net/socket.c处的300行我们可以看到
static struct vfsmount *sock_mnt __read_mostly;
static struct file_system_type sock_fs_type = {
.name = "sockfs",
.get_sb = sockfs_get_sb,
.kill_sb = kill_anon_super,
};
struct vfsmount *
do_kern_mount(const char *fstype, int flags, const char *name, void *data)
{
struct file_system_type *type = get_fs_type(fstype);
struct vfsmount *mnt;
if (!type)
return ERR_PTR(-ENODEV);
mnt = vfs_kern_mount(type, flags, name, data);
if (!IS_ERR(mnt) && (type->fs_flags & FS_HAS_SUBTYPE) &&
!mnt->mnt_sb->s_subtype)
mnt = fs_set_subtype(mnt, fstype);
put_filesystem(type);
return mnt;
}
int sock_create(int family, int type, int protocol, struct socket **res)
{
return __sock_create(current->nsproxy->net_ns, family, type, protocol, res, 0);
}
static int __sock_create(struct net *net, int family, int type, int protocol,
struct socket **res, int kern)
{
int err;
struct socket *sock;
const struct net_proto_family *pf;
/*
* Check protocol is in range
*/
if (family < 0 || family >= NPROTO)
return -EAFNOSUPPORT;
if (type < 0 || type >= SOCK_MAX)
return -EINVAL;
/* Compatibility.
This uglymoron is moved from INET layer to here to avoid
deadlock in module load.
*/
if (family == PF_INET && type == SOCK_PACKET) {
static int warned;
if (!warned) {
warned = 1;
printk(KERN_INFO "%s uses obsolete (PF_INET,SOCK_PACKET)/n",
current->comm);
}
family = PF_PACKET;
}
err = security_socket_create(family, type, protocol, kern);
if (err)
return err;
- 3-socket的实践到内核--追踪socket到内核
- 3-socket的实践到内核--追踪socket到内核 .
- 4-socket的实践到内核--追踪socket的创建
- 5-socket的实践到内核--追踪Unix的socket
- 4-socket的实践到内核--追踪socket的创建 .
- 5-socket的实践到内核--追踪Unix的socket .
- 1--socket的实践到内核--socket实践练习
- 1--socket的实践到内核--socket实践练习 .
- 7-socket的实践到内核--socket的监听
- 14-socket的实践到内核--socket的关闭
- 7-socket的实践到内核--socket的监听 .
- 14-socket的实践到内核--socket的关闭
- 9-socket的实践到内核--client调用connect
- 9-socket的实践到内核--client调用connect .
- 10-socket的实践到内核--UDP的socket数据的接收
- 11-socket的实践到内核--UDP的socket数据的发送
- 12-socket的实践到内核--TCP的socket数据的接收
- 13-socket的实践到内核--TCP的socket数据的发送
- Tomcat的启动过程
- 1--socket的实践到内核--socket实践练习
- sql之left join、right join、inner join的区别
- 2-socket的实践到内核--socket使用IP地址通讯
- if [-e $file]
- 3-socket的实践到内核--追踪socket到内核
- 用gcc编译C++程序
- android官方文档:ubuntu10.04下编译android源码Building the System
- GridView(九宫图)
- 4-socket的实践到内核--追踪socket的创建
- 点滴记录,以求共勉
- 浅析C#深拷贝与浅拷贝
- .bash_profile和.bashrc
- ImportExcelToDataTable