技术标签: linux socket bind 内核详解
socket系列文章都是承接第一篇socket创建,因此这里的编号和内核版本都继承了第一篇文章。
2. SYSCALL_DEFINE3函数
Bind系统调用通过SYSCALL_DEFINE3调用各个协议不同的bind函数,
SYSCALL_DEFINE3(bind,
int, fd, struct sockaddr __user *, umyaddr, int, addrlen)
{
struct
socket *sock;
struct
sockaddr_storage address;
int
err, fput_needed;
/*根据文件描述符fd,查找到对应的套接字socket*/
sock
= sockfd_lookup_light(fd, &err, &fput_needed);
if
(sock) {
err
= move_addr_to_kernel(umyaddr, addrlen, &address);
if
(err >= 0) {
err
= security_socket_bind(sock,
(struct sockaddr *)&address,
addrlen);
if
(!err)
err = sock->ops->bind(sock,
(struct sockaddr *)
&address, addrlen);
}
fput_light(sock->file,
fput_needed);
}
return
err;
}
(1)sock->ops->bind在创建TCP类型的socket时,进行了下面的赋值初始化操作,这里的bind定位为inet_bind()函数。
onst struct proto_ops inet_stream_ops = {
.family
= PF_INET,
.owner
= THIS_MODULE,
.release =
inet_release,
.bind = inet_bind,
.connect =
inet_stream_connect,
.socketpair
= sock_no_socketpair,
.accept
= inet_accept,
.getname
= inet_getname,
.poll
= tcp_poll,
.ioctl = inet_ioctl,
.listen = inet_listen,
.shutdown
= inet_shutdown,
.setsockopt =
sock_common_setsockopt,
.getsockopt =
sock_common_getsockopt,
.sendmsg
= inet_sendmsg,
.recvmsg =
inet_recvmsg,
.mmap
= sock_no_mmap,
.sendpage
= inet_sendpage,
.splice_read =
tcp_splice_read,
#ifdef CONFIG_COMPAT
.compat_setsockopt
= compat_sock_common_setsockopt,
.compat_getsockopt
= compat_sock_common_getsockopt,
.compat_ioctl =
inet_compat_ioctl,
#endif
2.1 sockfd_lookup_light函数
static
struct socket *sockfd_lookup_light(int fd, int *err, int *fput_needed)
{
struct
fd f = fdget(fd);//通过fd获取到struct fd结构体,然后获取file
struct
socket *sock;
*err
= -EBADF;
if
(f.file) {
sock
= sock_from_file(f.file, err);//返回套接字所对应的指针,存储在file->private_data;在sock_alloc_file函数中对其进行赋值
if
(likely(sock)) {
*fput_needed
= f.flags;
return
sock;//返回socket结构体指针
}
fdput(f);
}
return
NULL;
}
2.2 inet_bind函数
bind系统调用通过套接口层Inet_bind(),然后便会调用传输接口层的函数,TCP中的传输层接口函数为inet_csk_get_port函数,该函数主要实现bind的作用,如果用户系统调用使用的端口号为0,系统会自动选择一个可用的端口号,这里选择可用端口号思路是:先在绑定表中选择可用的端口号,如果在绑定表中没有可用的端口号,再选择空闲的端口号。
在af_inet.c文件中。
int inet_bind(struct socket *sock, struct
sockaddr *uaddr, int addr_len)
{
struct
sockaddr_in *addr = (struct sockaddr_in *)uaddr;//要绑定的sockaddr_in结构体
struct
sock *sk = sock->sk;
struct
inet_sock *inet = inet_sk(sk);
unsigned
short snum;//要绑定的端口
int
chk_addr_ret;//地址类型
int
err;
/*
If the socket has its own bind function then use it. (RAW)对于RAW类型的socket,调用raw socket自己的bind函数raw_bind*/
if
(sk->sk_prot->bind) {
err
= sk->sk_prot->bind(sk, uaddr, addr_len);
goto
out;
}
err
= -EINVAL;
if
(addr_len < sizeof(struct sockaddr_in))//sockaddr_in长度错误
goto
out;
chk_addr_ret
= inet_addr_type(sock_net(sk), addr->sin_addr.s_addr);//地址类型检查,看看是否回环地址,多播地址,组播地址,在下面的判断中需要使用到
/*
Not specified by any standard per-se, however it breaks too
* many applications when removed. It is unfortunate since
* allowing applications to make a non-local
bind solves
* several problems with systems using dynamic
addressing.
* (ie. your servers still start up even if
your ISDN link
* is
temporarily down)
sysctl_ip_nonlocal_bind表明是否允许绑定非本地的IP地址,默认为0,不允许绑定/proc/sys/net/ipv4# cat ip_nonlocal_bind
0
上面的那段注释说明了使用非本地地址绑定可以解决一些使用动态地址绑定的服务器程序,所以这个实现还是有实际意义的
inet->freebind是通过do_ip_setsockopt函数进行设置的,默认值为1,该值表示允许绑定一个非本地IP地址和不存在的IP地址,可以通过IP_FREEBIND设置
inet->transparent:其含义就是可以使一个服务器程序侦听所有的IP地址,哪怕不是本机的IP地址
*/
err
= -EADDRNOTAVAIL;
if
(!sysctl_ip_nonlocal_bind &&
!(inet->freebind ||
inet->transparent) &&
addr->sin_addr.s_addr !=
htonl(INADDR_ANY) &&
chk_addr_ret != RTN_LOCAL &&
chk_addr_ret != RTN_MULTICAST &&
chk_addr_ret != RTN_BROADCAST)
goto
out;
snum
= ntohs(addr->sin_port);//获取绑定的端口号
err
= -EACCES;
/*如果要绑定0-1023以下的端口号,需要用户具有CAP_NET_BIND_SERVICE权限PROT_SOCK就是1024*/
if
(snum && snum < PROT_SOCK && !capable(CAP_NET_BIND_SERVICE))
goto
out;
/* We keep a pair of addresses. rcv_saddr is
the one
*
used by hash lookups, and saddr is used for transmit.
*
*
In the BSD API these are the same except where it
*
would be illegal to use them (multicast/broadcast) in
*
which case the sending device address is used.
*/
lock_sock(sk);
/*
Check these errors (active socket, double bind). */
err
= -EINVAL;
if
(sk->sk_state != TCP_CLOSE || inet->num) //判断sk_state的状态是否为TCP_CLOSE,在创建socket时,sk_state初始为TCP_CLOSE,如果不等于TCP_CLOSE说明已经bind过,而num只有当rawsocket时才会不为0
goto
out_release_sock;
inet->rcv_saddr
= inet->saddr = addr->sin_addr.s_addr;//需要绑定的地址
if
(chk_addr_ret == RTN_MULTICAST || chk_addr_ret == RTN_BROADCAST)
inet->saddr
= 0; /* Use device */
/*
Make sure we are allowed to bind here.调用四层的bind函数,对于TCP来说就是inet_csk_get_port */
if (sk->sk_prot->get_port(sk, snum)) {
inet->saddr
= inet->rcv_saddr = 0;
err
= -EADDRINUSE;
goto
out_release_sock;
}
if
(inet->rcv_saddr)
sk->sk_userlocks
|= SOCK_BINDADDR_LOCK;//设置sk中的sk->userlocks表示绑定地址
if
(snum)
sk->sk_userlocks
|= SOCK_BINDPORT_LOCK;//设置sk中的sk->userlocks表示绑定端口
inet->sport
= htons(inet->num);
inet->daddr
= 0;
inet->dport
= 0;
sk_dst_reset(sk);
err
= 0;
out_release_sock:
release_sock(sk);
out:
return
err;
}
2.2.1在raw.c文件中的proto架构体的定义如下:
struct proto raw_prot = {
.name = "RAW",
.owner
= THIS_MODULE,
.close = raw_close,
.destroy =
raw_destroy,
.connect =
ip4_datagram_connect,
.disconnect =
udp_disconnect,
.ioctl = raw_ioctl,
.init
= raw_init,
.setsockopt =
raw_setsockopt,
.getsockopt =
raw_getsockopt,
.sendmsg
= raw_sendmsg,
.recvmsg =
raw_recvmsg,
.bind = raw_bind,
.backlog_rcv =
raw_rcv_skb,
.release_cb =
ip4_datagram_release_cb,
.hash = raw_hash_sk,
.unhash
= raw_unhash_sk,
.obj_size =
sizeof(struct raw_sock),
.h.raw_hash =
&raw_v4_hashinfo,
#ifdef CONFIG_COMPAT
.compat_setsockopt
= compat_raw_setsockopt,
.compat_getsockopt
= compat_raw_getsockopt,
.compat_ioctl =
compat_raw_ioctl,
#endif
};
对于该类型的proto没有bind函数
struct proto tcp_prot = {
.name = "TCP",
.owner = THIS_MODULE,
.close = tcp_close,
.connect = tcp_v4_connect,
.disconnect = tcp_disconnect,
.accept = inet_csk_accept,
.ioctl = tcp_ioctl,
.init = tcp_v4_init_sock,
.destroy = tcp_v4_destroy_sock,
.shutdown = tcp_shutdown,
.setsockopt = tcp_setsockopt,
.getsockopt = tcp_getsockopt,
.recvmsg = tcp_recvmsg,
.backlog_rcv = tcp_v4_do_rcv,
.hash = inet_hash,
.unhash = inet_unhash,
.get_port =
inet_csk_get_port,
.enter_memory_pressure = tcp_enter_memory_pressure,
.sockets_allocated = &tcp_sockets_allocated,
.orphan_count = &tcp_orphan_count,
.memory_allocated = &tcp_memory_allocated,
.memory_pressure = &tcp_memory_pressure,
.sysctl_mem = sysctl_tcp_mem,
.sysctl_wmem = sysctl_tcp_wmem,
.sysctl_rmem = sysctl_tcp_rmem,
.max_header = MAX_TCP_HEADER,
.obj_size = sizeof(struct tcp_sock),
.slab_flags = SLAB_DESTROY_BY_RCU,
.twsk_prot = &tcp_timewait_sock_ops,
.rsk_prot = &tcp_request_sock_ops,
.h.hashinfo =
&tcp_hashinfo,
#ifdef CONFIG_COMPAT
.compat_setsockopt = compat_tcp_setsockopt,
.compat_getsockopt = compat_tcp_getsockopt,
#endif
};
文章浏览阅读3.4k次,点赞8次,收藏42次。一、什么是内部类?or 内部类的概念内部类是定义在另一个类中的类;下面类TestB是类TestA的内部类。即内部类对象引用了实例化该内部对象的外围类对象。public class TestA{ class TestB {}}二、 为什么需要内部类?or 内部类有什么作用?1、 内部类方法可以访问该类定义所在的作用域中的数据,包括私有数据。2、内部类可以对同一个包中的其他类隐藏起来。3、 当想要定义一个回调函数且不想编写大量代码时,使用匿名内部类比较便捷。三、 内部类的分类成员内部_成员内部类和局部内部类的区别
文章浏览阅读118次。分布式系统要求拆分分布式思想的实质搭配要求分布式系统要求按照某些特定的规则将项目进行拆分。如果将一个项目的所有模板功能都写到一起,当某个模块出现问题时将直接导致整个服务器出现问题。拆分按照业务拆分为不同的服务器,有效的降低系统架构的耦合性在业务拆分的基础上可按照代码层级进行拆分(view、controller、service、pojo)分布式思想的实质分布式思想的实质是为了系统的..._分布式系统运维工具
文章浏览阅读174次。1.数据源准备2.数据处理step1:数据表处理应用函数:①VLOOKUP函数; ② CONCATENATE函数终表:step2:数据透视表统计分析(1) 透视表汇总不同渠道用户数, 金额(2)透视表汇总不同日期购买用户数,金额(3)透视表汇总不同用户购买订单数,金额step3:讲第二步结果可视化, 比如, 柱形图(1)不同渠道用户数, 金额(2)不同日期..._exce l趋势分析数据量
文章浏览阅读3.3k次。堡垒机可以为企业实现服务器、网络设备、数据库、安全设备等的集中管控和安全可靠运行,帮助IT运维人员提高工作效率。通俗来说,就是用来控制哪些人可以登录哪些资产(事先防范和事中控制),以及录像记录登录资产后做了什么事情(事后溯源)。由于堡垒机内部保存着企业所有的设备资产和权限关系,是企业内部信息安全的重要一环。但目前出现的以下问题产生了很大安全隐患:密码设置过于简单,容易被暴力破解;为方便记忆,设置统一的密码,一旦单点被破,极易引发全面危机。在单一的静态密码验证机制下,登录密码是堡垒机安全的唯一_horizon宁盾双因素配置
文章浏览阅读7.7k次,点赞4次,收藏16次。Chrome作为一款挺不错的浏览器,其有着诸多的优良特性,并且支持跨平台。其支持(Windows、Linux、Mac OS X、BSD、Android),在绝大多数情况下,其的安装都很简单,但有时会由于网络原因,无法安装,所以在这里总结下Chrome的安装。Windows下的安装:在线安装:离线安装:Linux下的安装:在线安装:离线安装:..._chrome linux debian离线安装依赖
文章浏览阅读153次。中国发达城市榜单每天都在刷新,但无非是北上广轮流坐庄。北京拥有最顶尖的文化资源,上海是“摩登”的国际化大都市,广州是活力四射的千年商都。GDP和发展潜力是衡量城市的数字指...
文章浏览阅读3.3k次。前言spark在java使用比较少,多是scala的用法,我这里介绍一下我在项目中使用的代码配置详细算法的使用请点击我主页列表查看版本jar版本说明spark3.0.1scala2.12这个版本注意和spark版本对应,只是为了引jar包springboot版本2.3.2.RELEASEmaven<!-- spark --> <dependency> <gro_使用java调用spark注册进去的程序
文章浏览阅读4.8k次。汽车零部件开发工具巨头V公司全套bootloader中UDS协议栈源代码,自己完成底层外设驱动开发后,集成即可使用,代码精简高效,大厂出品有量产保证。:139800617636213023darcy169_uds协议栈 源代码
文章浏览阅读4.6k次,点赞20次,收藏148次。AUTOSAR基础篇之OS(下)前言首先,请问大家几个小小的问题,你清楚:你知道多核OS在什么场景下使用吗?多核系统OS又是如何协同启动或者关闭的呢?AUTOSAR OS存在哪些功能安全等方面的要求呢?多核OS之间的启动关闭与单核相比又存在哪些异同呢?。。。。。。今天,我们来一起探索并回答这些问题。为了便于大家理解,以下是本文的主题大纲:[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JCXrdI0k-1636287756923)(https://gite_autosar 定义了 5 种多核支持类型
文章浏览阅读2.2k次,点赞6次,收藏14次。原因:自己写的头文件没有被加入到方案的包含目录中去,无法被检索到,也就无法打开。将自己写的头文件都放入header files。然后在VS界面上,右键方案名,点击属性。将自己头文件夹的目录添加进去。_vs2013打不开自己定义的头文件
文章浏览阅读3.3w次,点赞80次,收藏342次。此时,可以将系统中所有用户的 Session 数据全部保存到 Redis 中,用户在提交新的请求后,系统先从Redis 中查找相应的Session 数据,如果存在,则再进行相关操作,否则跳转到登录页面。此时,可以将系统中所有用户的 Session 数据全部保存到 Redis 中,用户在提交新的请求后,系统先从Redis 中查找相应的Session 数据,如果存在,则再进行相关操作,否则跳转到登录页面。当数据量很大时,count 的数量的指定可能会不起作用,Redis 会自动调整每次的遍历数目。_redis命令
文章浏览阅读449次,点赞3次,收藏3次。URP的设计目标是在保持高性能的同时,提供更多的渲染功能和自定义选项。与普通项目相比,会多出Presets文件夹,里面包含着一些设置,包括本色,声音,法线,贴图等设置。全局只有主光源和附加光源,主光源只支持平行光,附加光源数量有限制,主光源和附加光源在一次Pass中可以一起着色。URP:全局只有主光源和附加光源,主光源只支持平行光,附加光源数量有限制,一次Pass可以计算多个光源。可编程渲染管线:渲染策略是可以供程序员定制的,可以定制的有:光照计算和光源,深度测试,摄像机光照烘焙,后期处理策略等等。_urp渲染管线