socket里面那个又爱又恨的锁
查一个问题:结果看了一下软中断以及系统 所耗cpu,心中满是伤痕啊-------
perf 结果一眼可以看到:主要是锁
那么这个lock 是用来干什么的呢??
A:TCP socket的使用者有两种:进程(线程)和软中断。同一时间可能会有两个进程(线程),或位于不同CPU的两个软中断,或进程(线程)与软中断访问同一个socket。所以为了使socket在同一时刻只能被一个使用者访问,那么互斥机制是如何实现的呢?----是使用锁完成的, 也就是这个锁lock sock
struct sock {
...
socket_lock_t sk_lock;
...
}
/* This is the per-socket lock. The spinlock provides a synchronization
* between user contexts and software interrupt processing, whereas the
* mini-semaphore synchronizes multiple users amongst themselves.
*/
typedef struct {
spinlock_t slock;//该自旋锁是用于同步进程上下文和软中断上下文的关键;
int owned;//取值为1表示该传输控制块已经被进程上下文锁定,取值为0表示没有被进程上下文锁定;
wait_queue_head_t wq;//wq:等待队列,当进程上下文需要持有该传输控制块,但是其当前又被软中断锁定时,进程会等待
/*
* We express the mutex-alike socket_lock semantics
* to the lock validator by explicitly managing
* the slock as a lock variant (in addition to
* the slock itself):
*/
#ifdef CONFIG_DEBUG_LOCK_ALLOC
struct lockdep_map dep_map;
#endif
} socket_lock_t;
进程上下文的访问操作
进程上下文在访问该传输控制块之前需要调用lock_sock()锁定,在访问完成后调用release_sock()将其释放
//__lock_sock()将进程挂到sk->sk_lock中的等待队列wq上,直到没有进程再持有该该传输
//控制块时返回。注意:调用时已经持有sk->sk_lock,睡眠之前释放锁,返回前再次持有锁
static void __lock_sock(struct sock *sk)
{
//定义一个等待队列结点
DEFINE_WAIT(wait); //循环,直到sock_owned_by_user()返回0才结束
for (;;) {
//将调用进程挂接到锁的等待队列中
prepare_to_wait_exclusive(&sk->sk_lock.wq, &wait,
TASK_UNINTERRUPTIBLE);
//释放锁并打开下半部
spin_unlock_bh(&sk->sk_lock.slock);
//执行一次调度
schedule();
//再次被调度到时会回到这里,首先持锁并关闭下半部
spin_lock_bh(&sk->sk_lock.slock);
//如果没有进程再次持有该传输控制块,那么返回
if (!sock_owned_by_user(sk))
break;
}
finish_wait(&sk->sk_lock.wq, &wait);
} void lock_sock_nested(struct sock *sk, int subclass)
{
might_sleep();//调用lock_sock()可能会导致休眠---------注意
spin_lock_bh(&sk->sk_lock.slock);//持有自旋锁并关闭下半部
//如果owned不为0,说明有进程持有该传输控制块,调用__lock_sock()等待,挂在等待队列上休眠
if (sk->sk_lock.owned)
__lock_sock(sk);
//上面__lock_sock()返回后现场已经被还原,即持有锁并且已经关闭下半部。
//将owned设置为1,表示本进程现在持有该传输控制块
sk->sk_lock.owned = 1;
//释放锁但是没有开启下半部-----还是关闭了 软中断
spin_unlock(&sk->sk_lock.slock);
/*
* The sk_lock has mutex_lock() semantics here:------------这是干啥?
We express the mutex-alike socket_lock semanticsto the lock validator by explicitly managingthe slock as a lock variant
(in addition tothe slock itself): ------不懂*/
mutex_acquire(&sk->sk_lock.dep_map, subclass, 0, _RET_IP_); local_bh_enable();//开启下半部 软中断
}
owned为1之后不再持有自旋锁,也已经开启软中断。-----作用是协议栈的处理并非立刻就能结束,如果只是简单的在开始起持有自旋锁并关闭下半部,在处理结束时释放自旋锁并打开下半部,会降低系统性能,同时长时间关闭软中断,还可能使得网卡接收软中断得不到及时调用,导致丢包
release_sock()
进程上下文在结束传输控制块的操作之后,需要调用release_sock()释放传输控制块。释放的核心是将owned设置为0并通知其它等待该传输控制块的进程
void release_sock(struct sock *sk)
{
/*
* The sk_lock has mutex_unlock() semantics:
*/
//调试相关,忽略
mutex_release(&sk->sk_lock.dep_map, 1, _RET_IP_); //获取自旋锁并关闭下半部
spin_lock_bh(&sk->sk_lock.slock);
//如果后备队列不为空,则调用__release_sock()处理后备队列中的数据包,见数据包的接收过程
if (sk->sk_backlog.tail)
__release_sock(sk);
//设置owned为0,表示调用者不再持有该传输控制块
sk->sk_lock.owned = 0;
//如果等待队列不为空,则唤醒这些等待的进程
if (waitqueue_active(&sk->sk_lock.wq))
wake_up(&sk->sk_lock.wq);
//释放自旋锁并开启下半部
spin_unlock_bh(&sk->sk_lock.slock);
}
(1)软中断先访问进程后访问
这时软中断已经获取了自旋锁,进程在获取自旋锁时会等待,软中断释放锁时进程才能成功获取锁。
(2)进程先访问软中断后访问
进程获取自旋锁(关软中断,防止被软中断打断)时会将sk->sk_lock.owned设置为1后释放自旋锁并开启软中断,然后执行对socket的访问。这时如果软中断发生,则进程的执行被中止,然后软中断中 将数据放到接收后备队列中
int tcp_v4_rcv(struct sk_buff *skb)
{
...
process:
...
//获取sk->sk_lock.slock自旋锁
bh_lock_sock_nested(sk);
//如果没有进程锁定该传输控制块,将数据接收到奥prequeue或者receive_queue中
if (!sock_owned_by_user(sk)) {
if (!tcp_prequeue(sk, skb))
ret = tcp_v4_do_rcv(sk, skb);
} else
//如果进程已经锁定该传输控制块,那么先将数据接收到后备队列中----赶紧退出 让进程处理 然后在release的时候 处理后备队列
sk_add_backlog(sk, skb);
//释放自旋锁
bh_unlock_sock(sk);
... /* BH context may only use the following locking interface. */
#define bh_lock_sock(__sk) spin_lock(&((__sk)->sk_lock.slock))
#define bh_lock_sock_nested(__sk) \
spin_lock_nested(&((__sk)->sk_lock.slock), \
SINGLE_DEPTH_NESTING)
#define bh_unlock_sock(__sk) spin_unlock(&((__sk)->sk_lock.slock))
所以这个锁 貌似 规避不了,那么怎么处理呢???
socket里面那个又爱又恨的锁的更多相关文章
- 又爱又恨系列之枚举enum
其实枚举挺简单的,只不过以前没好好学,所以不知道这个东西,恩,现在梳理一下 整体而言,首先枚举是一个数据类型,这个数据类型和结构体有点像 可以分为三个层次 1.枚举数据类型定义 第一种:enum 枚举 ...
- 谈谈Nancy中让人又爱又恨的Diagnostics【上篇】
前言 在Nancy中有个十分不错的功能-Diagnostics,可以说这个功能让人又爱又恨. 或许我们都做过下面这样的一些尝试: 记录某一个功能用到的相关技术信息 记录下网站的访问记录 全局配置某些框 ...
- Lambda-让人又爱又恨的“->"
写在前边 聊到Java8新特性,我们第一反应想到的肯定是Lambda表达式和函数式接口的出现.要说ta到底有没有在一定程度上"优化"了代码的简洁性呢?抑或是ta在一定程度上给程序员 ...
- 爱与恨的抉择:ASP.NET 5+EntityFramework 7
EF7 的纠缠 ASP.NET 5 的无助 忘不了你的好 一开始列出的这个博文大纲,让我想到了很久之前的一篇博文:恋爱虽易,相处不易:当EntityFramework爱上AutoMapper,只不过这 ...
- 让人又爱又恨的char(字符型)
今天来总结一下char型,平常写算法的时候对这个东西感觉都有一点绕着走,说到底还是对这部分的知识不熟悉所以有点怕他,不过以后不要怕,今天来总结一下 首先,说到字符型数据类型,char型,恩它是一种数据 ...
- 又爱又恨的BOOTSTRAP
搞本书,看了一天,确实,,UIKIT比它好用... 但,艺多不压身吧. 今天自己抄了个大概的,不用其它插件,,但那手风琴,真的找了很多,没有中意的... <!DOCTYPE html> & ...
- 一直又爱又恨的jqueryValidate,看到一个还不错的laber.error样式
默认样式,不是很好看 修改之后就高大上多了 功臣如下: label.error { position: absolute; right: 18px; top: 5px; colo ...
- 搞不懂为什么开发人员爱iOS恨Android?
导读:很多网站发表文章大同小异.唯有这个不同点,给大家分享. Android和iOS的较量一直都是人们津津乐道的话题.两个平台各有各的优势所在,同时也都力图能在各个方面赶超对手.对于用户来说,青菜萝卜 ...
- 技术谈 | SDN 和 NFV 之间的爱与恨
部分开发者经常混淆 SDN 和 NFV,无法看清他们的关系.今天,小编搬出华为技术专家的一篇大稿,给大家掰扯掰扯:SDN 和 NFV 究竟是什么关系. ----文/闫长江 什么是 SDN 回到基本的概 ...
随机推荐
- 搭建ipse隧道
我没有太多的物理服务器,实验环境只能用四台装了linux的虚拟机来模拟,用户层工具是openswan.大致拓扑如下(我有点懒,公网地址我用的194.168.10.0/24,别和192.168.xx.x ...
- 多测师讲解selenium_iframe框定位_高级讲师肖sir
iframe 框定位方法: 查看iframe框 京东点击登录定位元素 定位qq: qq登录定位的元素 查找iframe框 定位iframe框 from selenium import webdrive ...
- 多测师讲解pyhon__hashlib_高级讲师肖sir
一.加密,加密成16进制的字符串 # import hashlib # 导入hashlib模块# md = hashlib.md5() # 获取一个md5加密算法对象# md.update('需要加密 ...
- 【并查集】BZOJ 1854 连续攻击游戏
题目内容 洛谷链接 lxhgww最近迷上了一款游戏,在游戏里,他拥有很多的装备,每种装备都有2个属性,这些属性的值用[1,10000]之间的数表示.当他使用某种装备时,他只能使用该装备的某一个属性.并 ...
- sql中的Bulk 导入txt文本
通常,我们会对于一个文本文件数据导入到数据库中,不多说,上代码. 首先,表结构如下. 其次,在我当前D盘中有个文本文件名为2.txt的文件. 在数据库中,可以这样通过一句代码插入. Bulk in ...
- frida- registernatives获取so层动态注册函数
frida获取so层动态注册函数 谢谢大佬的无私奉献https://github.com/lasting-yang/frida_hook_libart 一.js模板一 function hook_Re ...
- 【API管理 APIM】APIM中如何配置使用URL路径的方式传递参数(如由test.htm?name=xxx 变为test\xxx)
问题描述 在默认的URL传递参数中,我们使用的是https://test01.azure-api.cn/echo/resource?param1=sample¶m2=testname这 ...
- MySql中varchar和char,如何选择合适的数据类型?
背景 学过MySQL的同学都知道MySQL中varchar和char是两种最主要的字符串类型,varchar是变长的类型,而char是固定长度.那关于如何选择类型就成为令人头疼的事,很多初学者为了保证 ...
- SpringMVC异常的处理机制
SpringMVC异常的处理机制 处理流程图 其本质还是把异常交给SpringMVC框架来处理 系统的dao.service.controller出现异常都通过throws Exception向上抛出 ...
- Linux入门到放弃之二《目录处理常用命令的使用方法》
一,目录操作命令 1.用pwd命令查看当前所在的目录: 2.用ls命令列出此目录下的文件和目录: 3.列出此目录下包括隐藏文件在内的所有文件和目录并且长格式显示: ( -l表示长格式,-a表示隐藏文 ...