追踪Linux TCPIP代码运行,01.追踪linux代码运行——socket创建
[前言]
一直想好好研究linux的网络实现,又不知从何入手,总是片面的学习,形不成体系。最近发现秦健老师的《追踪Linux TCP/IP代码运行》,觉得是个不错的入口点,于是决定借此一来整合一下自己零碎的知识,二来希望对节后的工作有些帮助,何乐而不为。本文仅仅是介绍从调用socket函数到成功返回一个socket文件描述符之间所涉及到的重要函数以及其调用关系、代码位置等,该过程涉及的重要数据结构以及相关知识会有专门的介绍。
[linux内核版本:3.10.25]
[一]从GLIBC封装的库函数到系统调用
我们平常在网络编程中调用的socket()函数,实为GLIBC封装的库函数,该库函数实际功能采用汇编语言实现,最后通过0x80号软中断陷入内核,调用真正的系统调用程序。
|>>socket():
|创建一个socket套接字
|int socket(int domain, int type, int protocol);
|glibc-2.20/sysdeps/unix/sysv/linux/i386/socket.S[43]
---|>> int $0x80:
---|0x80号软中断用来陷入内核,然后调用系统调用的总入口system_call
---|---|>> system_call():
---|---|系统调用的总入口,完全用汇编语言实现
---|---|asmlinkage int system_call(void);
---|---|linux-3.10.25/arch/x86/kernel/entry_64.S[608]
---|---|---|>>
sys_socketcall():
---|---|---|内核提供给socket通信的总入口
---|---|---|asmlinkage long sys_socketcall(int call,
unsigned long __user *args);
---|---|---|linux-3.10.25/net/socket.c[2456]
点击(此处)折叠或打开
SYSCALL_DEFINE2(socketcall, int, call, unsigned long __user *, args)
{
......
/* copy_from_user should be SMP safe. */
if (copy_from_user(a, args, len))
return -EFAULT;
......
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_accept4(a0, (struct sockaddr __user *)a1,
(int __user *)a[2], 0);
break;
[二]调用sys_socket创建socket和sock结构体
在Linux-3.10.25中socket系列函数(sys_xxx)在内核源码中都是用DEFINEX宏来定义,关于DEFINEX的定义请查看linux-3.10.25/include/linux/syscalls.h[170],至于为什么这样做不是我们关心的重点,使用这些宏转换后,这些函数的真面目与2.6系列内核中的定义没有什么不同,或者我们也可以直接查看这些函数在头文件中的声明来确定这些函数的原型。
|>>sys_socket():
|asmlinkage long
sys_socket(int family, int type, int protocol);
|SYSCALL_DEFINE3(socket,
int, family, int, type, int, protocol)
|linux-3.10.25/net/socket.c[1361]
---|>>
sock_create():
---|直接调用__scok_create()函数执行创建任务
---|int
sock_create(int family, int type, int protocol, struct socket **res)
---|linux-3.10.25/net/socket.c[1349]
---|---|>> __sock_create():
---|---|创建socket和sock结构体
---|---|int
__sock_create(struct net *net, int family, int type, int protocol, struct
socket **res, int kern)
---|---|linux-3.10.25/net/socket.c[1236]
---|---|---|>> sock_alloc() :
---|---|---|创建socket结构体
---|---|---|static
struct socket *sock_alloc(void)
---|---|---|linux-3.10.25/net/socket.c[531]
---|---|---|>>inet_create():
---|---|---|创建scok结构体,在__scok_create函数中的1311行被调用
---|---|---|static
int inet_create(struct net *net, struct socket *sock, int protocol, int
kern)
---|---|---|linux-3.10.25/net/ipv4/af_inet.c[275]
在__sock_create()函数中linux系统创建了两个重要的数据结构,socket和sock结构体。Sock结构体根据使用的具体协议挂入socket结构体中,同时sock结构体中又包含了指向sk_buff结构的指针。sk_buff这个结构相信大家并不陌生,每个协议都是用该结构体来封装、运载数据包,也就说每一个数据包都用一个sk_buff结构体来表示。
[三]调用sock_alloc()函数创建socket结构体
|>>
sock_alloc() :
|创建socket结构体
|static struct socket *sock_alloc(void)
|linux-3.10.25/net/socket.c[531]
---|>> new_inode_pseudo():
---|给指定的超级块结构申请一个inode
---|struct inode
*new_inode_pseudo(struct super_block *sb)
---| linux-3.10.25/fs/inode.c[904]
---|---|>> alloc_inode():
---|---|给指定的超级块结构申请一个inode
---|---|static
struct inode *alloc_inode(struct super_block *sb)
---|---|linux-3.10.25/fs/inode.c[202]
---|---|---|>>sock_alloc_inode() :
---|---|---|给指定的超级块结构申请一个inode,在alloc_inode函数中207行被调用
---|---|---|static
struct inode *sock_alloc_inode(struct super_block *sb)
---|---|---|linux-3.10.25/net/socket.c[241]
点击(此处)折叠或打开
static struct inode *alloc_inode(struct super_block *sb)
{
struct inode *inode;
if (sb->s_op->alloc_inode)
/* 我们在这里调用sock_alloc_inode函数来创建socket和inode */
inode = sb->s_op->alloc_inode(sb);
else
inode = kmem_cache_alloc(inode_cachep, GFP_KERNEL);
点击(此处)折叠或打开
/* linux-3.10.25/net/socket.c[299] */
static const struct super_operations sockfs_ops = {
.alloc_inode = sock_alloc_inode,
.destroy_inode = sock_destroy_inode,
.statfs = simple_statfs,
}
点击(此处)折叠或打开
/* linux-3.10.25/net/socket.c[241] */
static struct inode *sock_alloc_inode(struct super_block *sb)
{
struct socket_alloc *ei;
struct socket_wq *wq;
ei = kmem_cache_alloc(sock_inode_cachep, GFP_KERNEL);
if (!ei)
return NULL;
wq = kmalloc(sizeof(*wq), GFP_KERNEL);
if (!wq) {
kmem_cache_free(sock_inode_cachep, ei);
return NULL;
}
init_waitqueue_head(&wq->wait);
wq->fasync_list = NULL;
RCU_INIT_POINTER(ei->socket.wq, wq);
ei->socket.state = SS_UNCONNECTED;
ei->socket.flags = 0;
ei->socket.ops = NULL;
ei->socket.sk = NULL;
ei->socket.file = NULL;
return &ei->vfs_inode;
}
[四]调用inet_create()函数创建sock结构体
|>> inet_create() :
|创建并设置sock结构体,在__sock_create函数中1311行被调用
|static int
inet_create(struct net *net, struct socket *sock, int protocol, int kern)
|linux-3.10.25/net/ipv4/af_inet.c[275]
---|>>sk_alloc():
---|创建并初始化sock结构体
---|struct sock
*sk_alloc(struct net *net, int family, gfp_t priority, struct proto *prot)
---| linux-3.10.25/core/scok.c[1314]
---|---|>>sk_prot_alloc():
---|---|创建socket结构体
---|---|static
struct sock *sk_prot_alloc(struct proto *prot, gfp_t priority, int family)
---|---|linux-3.10.25/core/scok.c[1225]
点击(此处)折叠或打开
/* 在这里我们创建sock结构体 */
err = pf->create(net, sock, protocol, kern);
if (err < 0)
goto out_module_put;
点击(此处)折叠或打开
/* linux-3.10.25/net/ipv4/af_inet.c[1019] */
static const struct net_proto_family inet_family_ops = {
.family = PF_INET,
.create = inet_create,
.owner = THIS_MODULE,
};
点击(此处)折叠或打开
/* linux-3.10.25/net/ipv4/af_inet.c[275] */
static int inet_create(struct net *net, struct socket *sock, int protocol,
int kern)
{
struct sock *sk;
点击(此处)折叠或打开
struct sock *sk_alloc(struct net *net, int family, gfp_t priority,
struct proto *prot)
{
struct sock *sk;
sk = sk_prot_alloc(prot, priority | __GFP_ZERO, family);
if (sk) {
/* 如果sock结构体分配成功那么将在该函数做一些初始化工作 */
sk->sk_family = family;
/*
* See comment in struct sock definition to understand
* why we need sk_prot_creator -acme
*/
sk->sk_prot = sk->sk_prot_creator = prot;
sock_lock_init(sk);
sock_net_set(sk, get_net(net));
atomic_set(&sk->sk_wmem_alloc, 1);
sock_update_classid(sk);
sock_update_netprioidx(sk);
}
return sk;
追踪Linux TCPIP代码运行,01.追踪linux代码运行——socket创建相关推荐
- Linux应用编程基础01:Linux应用编程绪论
目录 1. 如何理解系统调用 1.1 系统调用的意义 1.2 系统调用服务例程 1.3 系统调用封装例程(wrapper routine) 2. 系统调用 / API / POSIX标准的关系 2.1 ...
- Linux查看历史信息代码,使用 GIT 获得Linux Kernel的代码并查看,追踪历史记录
Linux kernel 的官方 GIT地址是: 可以从这个地址拿到 kernel 的 代码仓库. 1. 拿代码仓库 git clone git://git.kernel.org/pub/scm/l ...
- Linux下安装 SkyWalking 分布式追踪系统
Linux下安装 SkyWalking 分布式追踪系统 1.SkyWalking简介 1.1 SkyWalking介绍 SkyWalking项目是由华为大牛吴晟开源的个人项目,目前已经加入Apache ...
- Linux Kernel 0.01 的编译和运行
Linux Kernel 0.01 的编译和运行 本文操作环境均在 Linux 系统中实现. ===================================================== ...
- 手把手教你如下在Linux下如何写一个C语言代码,编译并运行
文章目录 手把手教你如下在Linux下如何写一个C语言代码,编译并运行 打开Ubuntu终端 创建 helloworld.c 编译C文件 手把手教你如下在Linux下如何写一个C语言代码,编译并运行 ...
- Linux(Ubuntu)使用setsid命令后台运行python代码并记录终端输出,并实现开机自启
1.摘要 本文主要讲解:Linux(Ubuntu)使用setsid命令后台运行python代码并记录终端输出,并实现开机自启 主要思路: setsid命令 比nohub命令好用,setsid有些系统默 ...
- linux mysql 进程查看工具_linux查看正在运行的进程(如何在Linux中查看所有正在运行的进程)...
如何在Linux中查看所有正在运行的进程 名称:ps 使用权限:所有使用者 使用方式:ps [options] [--help] 说明:显示瞬间行程 (process) 的动态 参数:ps的参数非常多 ...
- Linux设备驱动程序(二)——建立和运行模块
文章目录 前言 一.设置测试系统 二.Hello World 模块 1.代码详解 2.执行效果 三.内核模块相比于应用程序 1.用户空间和内核空间 2.内核的并发 3.当前进程 4.几个别的细节 四. ...
- linux下能运行python,(转)Linux下运行python
原文: http://blog.csdn.net/jackywgw/article/details/48847187 在linux命令行下运行python,可以直接输出hello world jack ...
最新文章
- python 单元测试setup执行了多次_python单元测试setUp与tearDown
- MySql层级树查询
- 交换机的端口工作模式
- HTTP MIME type
- 用C语言打开文件的几种方式及区别
- co11n——生产订单确认的BAPI
- 如何计算机操作维护培训,电脑基本操作培训教材.ppt
- 余承东:华为自研的麒麟A1芯片已经应用在了多款可穿戴产品中
- ajax success function_【java 基础】java-回调函数(结合jquery.ajax)
- PCL之鼠标拾取点云的三维坐标
- vue-cli history 本地开发刷新页面丢失_react 开发入门
- python groupby agg_Python数据分析:探索性分析
- PHP Cookbook读书笔记 – 第17章图形
- 服务器保持与Mysql的连接
- 可视化程序设计基础(team)——采访上届大佬
- 【语音编码】基于matlab PCM编解码【含Matlab源码 555期】
- 机器视觉全球顶级实验室
- 分布式计算机系统概要
- win10系统计算机如何分盘,win10新电脑怎么合理分盘?给win10电脑合理分盘的设置方法...
- 【B2B】阿里巴巴汪海:1688成年礼—从中小企业数字化看B2B发展趋势