Linux内核网络协议栈5-socket地址绑定
一、socket绑定入口
struct sockaddr_in server_address;
server_address.sin_family = AF_INET;
server_address.sin_addr.s_addr = inet_addr("0.0.0.0");
server_address.sin_port = htons(9734);
server_len = sizeof(server_address);
bind(server_sockfd, (struct sockaddr *)&server_address, server_len);
2、绑定入口
前面介绍了socket从库函数到内核的过程,其最终都是通过102号中断进入内核,所不同的是子中断号不同;对于绑定,其子中断号是2;
和创建socket一样,绑定socket的处理函数都是:
asmlinkage long sys_socketcall(int call, unsigned long __user *args)
{ unsigned long a[6]; unsigned long a0, a1; int err; if (copy_from_user(a, args, nargs[call])) return -EFAULT; a0 = a[0]; a1 = a[1]; switch (call) { …... case SYS_BIND: err = sys_bind(a0, (struct sockaddr __user *)a1, a[2]); …...
}
二、绑定的具体过程
sys_bind()函数如下,一起来分析一下它的主要过程:
asmlinkage long sys_bind(int fd, struct sockaddr __user *umyaddr, int addrlen)
{ struct socket *sock; char address[MAX_SOCK_ADDR]; int err, fput_needed; // 1, 根据fd查找相应的socket结构 sock = sockfd_lookup_light(fd, &err, &fput_needed); if (sock) { // 2, 将用户空间的地址结构拷贝到内核空间 err = move_addr_to_kernel(umyaddr, addrlen, address); if (err >= 0) { err = security_socket_bind(sock,(struct sockaddr *)address,addrlen); if (!err) // 3, 根据协议域及socket类型,调用相应的bind函数 err = sock->ops->bind(sock,(struct sockaddr *)address, addrlen); } fput_light(sock->file, fput_needed); } return err;
}
1、根据fd找到相应的socket结构
在创建socket的最后,会将socket结构与文件系统关联,并返回给应用程序与socket相关的文件描述符;这里是根据应用程序传递过来的文件描述符取得关联的socket结构;
下面看看从fd取得socket结构的代码:
static struct socket *sockfd_lookup_light(int fd, int *err, int *fput_needed)
{ struct file *file; struct socket *sock; *err = -EBADF; file = fget_light(fd, fput_needed); if (file) { sock = sock_from_file(file, err); if (sock) return sock; fput_light(file, *fput_needed); } return NULL;
}
struct file *fget_light(unsigned int fd, int *fput_needed)
{ struct file *file; struct files_struct *files = current->files; …... file = fcheck_files(files, fd); …... return file;
}
在创建socket中提到,file与socket关联,是通过file->private=socket完成的,因为获取到file结构变量后,也可以通过同样的方式取得socket结构变量;sock_from_file()函数就是用来完成此工作的;
1) 用户空间和内核空间的概念:
Linux内核管理模型中,简化了分段机制,使得虚拟地址与线性地址总是一致的;因此,针对32位的机器,Linux的虚拟地址空间也为0~4G。
Linux内核将这4G字节的空间分为两部分:将最高的1G字节(从虚拟地址0xC0000000到0xFFFFFFFF),供内核使用,称为“内核空间”;而将较低的3G字节(从虚拟地址0x00000000到0xBFFFFFFF),供各个进程使用,称为“用户空间”。因为每个进程可以通过系统调用进入内核,因此,Linux内核由系统内的所有进程共享。但是从具体进程的角度来看,每个进程可以拥有4G字节的虚拟空间。
2) 用户态和内核态
当进程在执行用户自己的代码时,则称其处于用户运行态(用户态);即此时处理器在特权级最低的(3级)用户代码中运行;在用户态,进程使用进程的用户栈;
当进程执行系统调用而陷入内核代码中执行时,称该进程处于内核运行态(或简称为内核态),此时处理器处于特权级最高的(0级)内核代码中执行;当进程处于内核态时,执行的内核代码会使用当前进程的内核栈;每个进程都有自己的内核堆栈。
当正在执行用户程序而突然被中断程序中断时,此时用户程序也可以象征性地称为处于内核态,因为中断处理程序将使用当前进程的内核栈,这与处于内核态的进程的状态有些类似。
3) 用户栈和内核栈
前面提到,每个进程有2个栈,即用户栈和内核栈;用户栈的空间指向用户地址空间,内核栈的空间指向内核地址空间。当进程在用户态运行时,CPU堆栈指针寄存器esp指向用户栈地址,使用用户栈;当进程运行在内核态时,CPU堆栈指针寄存器esp指向的是内核栈空间地址,使用的是内核栈;
内核在创建一个新的进程时,在创建进程控制块的同时,即创建了内核栈;而当进程调用execve的时候,才会创建用户栈;
4) 为什么要拷贝?
如果内核直接访问用户空间的地址,或是使用memcpy来拷贝,可能会出现缺页,但是缺页后的中断处理程序需要特定的结构辅助才能正常返回到缺页中断发生的地方,因此需要使用copy_from_user来完成;
结合上面提到的用户态、内核态、用户空间和地址空间后,就不难理解为什么系统调用中,都要将一些参数从用户空间拷贝到内核空间了;
3、地址结构
示例代码中创建的是类型为struct sockaddr_in的结构体变量,在调用bind()库函数时,将地址变量强制转化为struct sockaddr结构;
大家看到这里可能会有下面的疑问:
a)这两个结构体到底是什么关系?
b)为什么要强制转化为struct sockaddr结构?
c)bind()库函数最后一个参数,为什么要把结构体长度传进去呢?
首先看看struct sockaddr_in和struct sockaddr结构体吧:
struct sockaddr_in { sa_family_t sin_family; /* Address family */ __be16 sin_port; /* Port number */ struct in_addr sin_addr; /* Internet address */ /* Pad to size of `struct sockaddr'. */ unsigned char __pad[__SOCK_SIZE__ - sizeof(short int) - sizeof(unsigned short int) - sizeof(struct in_addr)];
}; struct sockaddr { sa_family_t sa_family; /* address family, AF_xxx */ char sa_data[14]; /* 14 bytes of protocol address */
};
在struct sockaddr_in结构体中,__pad成员用于结构体的对齐,使struct sockaddr_in和struct sockaddr的大小一致;
Linux内核网络协议栈5-socket地址绑定相关推荐
- Linux内核网络协议栈8—socket监听
几个问题 了解以下几个问题的同学可以直接忽略下文: 1.listen 库函数主要做了什么? 2. 什么是最大并发连接请求数? 3.什么是等待连接队列? socket 监听相对还是比较简单的,先看 ...
- Linux内核网络协议栈:udp数据包发送(源码解读)
<监视和调整Linux网络协议栈:接收数据> <监控和调整Linux网络协议栈的图解指南:接收数据> <Linux网络 - 数据包的接收过程> <Linux网 ...
- Linux内核网络协议栈流程及架构
文章目录 Linux内核网络报文处理流程 Linux内核网络协议栈架构 Linux内核网络报文处理流程 linux网络协议栈是由若干个层组成的,网络数据的处理流程主要是指在协议栈的各个层之间的传递. ...
- 深入浅出Linux内核网络协议栈|结构sk_buff|Iptables|Netfilter丨内核源码丨驱动开发丨内核开发丨C/C++Linux服务器开发
深入浅出Linux内核网络协议栈 视频讲解如下,点击观看: 深入浅出Linux内核网络协议栈|结构sk C/C++Linux服务器开发高级架构师知识点精彩内容包括:C/C++,Linux,Nginx, ...
- Linux内核网络协议栈7-socket端口管理
一.前情回顾 上一节<socket 地址绑定 >中提到,应用程序传递过来的端口在内核中需要检查端口是否可用: if (sk->sk_prot->get_port(sk, snu ...
- Linux内核网络协议栈
一.注册时机 1.在内核初始化时完成: 2.内核初始化过程(init/main.c):kernel_init()->do_basic_setup()->do_initcalls()-> ...
- linux内核网络协议栈--linux网络设备理解(十三)
网络层次 linux网络设备驱动与字符设备和块设备有很大的不同. 字符设备和块设备对应/dev下的一个设备文件.而网络设备不存在这样的设备文件.网络设备使用套接字socket访问,虽然也使用read, ...
- Linux 内核网络协议栈运行原理
封装:当应用程序用 TCP 协议传送数据时,数据首先进入内核网络协议栈中,然后逐一通过 TCP/IP 协议族的每层直到被当作一串比特流送入网络.对于每一层而言,对收到的数据都会封装相应的协议首部信息( ...
- Linux内核网络协议栈6-socket地址绑定(2)
三.根据不同的协议来完成绑定 上面代码中的第3步是根据应用程序在创建socket时传递到内核的协议域及socket类型来决定调用采用哪个方法,具体可以参考 创建socket 一文,这里不再赘 ...
最新文章
- Linux系统上用Sigil创建和编辑 EPUB 文件
- mat工具MemoryAnalyzer进行分析java内存溢出hprof文件
- pyspark报错java.lang.ClassNotFoundException: com.mysql.cj.jdbc.Driver
- UIApplication的作用
- java list 在头部添加6_【Java提高十六】集合List接口详解
- c++语言 xml数据绑定技术简介
- mysql 二进制 查询_MySql如何插入和查询二进制数据_MySQL
- Android类参考---Fragment(二)
- WSO2流处理器相关学习(1)快速入门指南【转】
- iptables中DNAT、SNAT和MASQUERADE的理解及限速syn
- URP教务系统自动登录
- DJ13-1 汇编语言程序设计-3
- 前端构建工具Gulp的学习和使用
- 如何从一个完全自动化的交易系统获利
- iOS dismissViewController到指定页面或dismiss多层
- GitHub Action入门简介
- 百度智能云体验馆领取CDN及24小时急速收录
- Blinker控制esp8266 01s实现远程控制继电器开关代码
- mysql易百_MySQL Replace语句
- http post 和 get
热门文章
- div中的p标签于img设置同一水平_HTML网页制作常用标签及说明——前端开发入门...
- php同步邮件,php – 使用同步驱动程序在Laravel 4中排队电子邮件
- 实验四 lr0分析程序的设计与实现_试验机海外直播丨实现高精度CAE分析实验的材料评价案例技术介绍...
- linux c 网络事件 通知,深入理解Linux网络技术内幕—通知链
- 原子动作检测 A Better Baseline for AVA
- linux中 ls |wc -l
- 测绘专业学生对测绘软件的一点认识
- hdu-2717(基础搜索bfs)
- 51nod 1693 水群
- 运输层的多路复用于多路分解