存档

‘*nix技术’ 分类的存档

死锁检测模块lockdep简介

2013年4月10日 没有评论 26,619 次浏览

在Linux系统里,假设有两处代码(比如不同线程的两个函数F1和F2)都要获取两个锁(分别为L1和L2),如果F1持有L1后再去获取L2,而此时恰好由F2持有L2且它也正在尝试获取L1,那么此时就是处于死锁的状态,这是一个最简单的死锁例子,也即所谓的AB-BA死锁。 死锁导致的最终结果无需多说,关于如何避免死锁在教科书上也有提到(参考1),最简单直观的做法就是按顺序上锁,以破坏死锁的环形等待条件。但对于拥有成千上万个锁的整个系统来说,完全定义它们之间的顺序是非常困难的,所以一种更可行的办法就是尽量提前发现这其中潜在的死锁风险,而不是等到最后真正出现死锁时给用户带来切实的困惑。 已有很多工具用于发现可能的死锁风险,而本文介绍的调试/检测模块lockdep,即是属于这一类工具的一种。调试模块lockdep从2006年(https://lwn.net/Articles/185666/)引入内核,经过实践验证,其对提前发现死锁起到了巨大的效果(http://lwn.net/Articles/321670/)。 官方文档(完全参考2)有介绍调试模块lockdep的设计原理,这里按照我自己的理解描述一下。 一,lockdep操作的基本单元并非单个的锁实例,而是锁类(lock-class)。比如,struct... [阅读更多]

中断栈溢出后的结果

2013年3月24日 没有评论 24,209 次浏览

说一下上文中最开始提到的“某个问题”:如果一台主机网卡比较多,然后每个网卡分队列又比较多,总之结果就是系统里的网卡设备的中断号比较多(关于超过256个中断数的情况,请见参考1,2,3),一旦所有这些中断都绑定到同一个CPU,那么如果网卡收到数据包进而触发中断,而众多硬中断一嵌套就非常容易出现中断栈溢出。一旦中断栈溢出,那么将会导致怎样的结果,这曾在之前的文章里隐含的提到过,这里再重新整理一遍。 在继续下面的描述之前,先看两个知识点: 1,Linux... [阅读更多]

Linux文件系统的日志功能

2013年3月17日 1 条评论 28,012 次浏览

所谓原子操作,即要么全部成功,要么全部失败。磁盘硬件可以保证对一个扇区的操作是原子的,也就是说对一个磁盘扇区进行写操作,那么结果只有两种:1,整个扇区被完全修改为新数据;2,整个扇区没有被做任何修改;而不会出现扇区半新半旧的混乱状态(比如前256字节为新数据,后256字节还为旧数据,这里假定一个扇区为512字节)。 如果需要对超过一个扇区的操作进行原子控制,那么就需要更高一级别的软件控制机制。这是必须的,否则将导致磁盘文件系统数据的不一致错误,在Linux上对应的软件控制机制为JBD(Journaling... [阅读更多]

分类: *nix技术, 文件系统 标签: , , ,

对Linux x86-64架构上硬中断的重新认识

2013年3月16日 1 条评论 11,812 次浏览

对于x86硬中断的概念,一直都落在理论的认识之上,直到这两天才(因某个问题)发现Linux的实现却并非如此,这里纠正一下(注意:Linux内核源码更新太快,一个说法的时效性太短,所以需注意我提到的香草内核版本,并且以x86-64架构为基准)。 以前的认识:Linux对硬中断(本文如无特殊说明,都是指普通意义上的可屏蔽硬件中断)的处理有优先级概念,高优先级硬中断可以打断低优先级硬中断。 重新认识: 1,对于x86硬件而言,在文档325462.pdf卷3章节6.9... [阅读更多]

如何对仅在指定调用路径下的函数断下断点

2013年3月16日 没有评论 11,908 次浏览

举个例子,有示例代码如下: # cat t.c #include <stdio.h> void foo() { printf("foo\n"); } void bar() { foo(); } void baz() { foo(); } int main() { bar(); baz(); bar(); ... [阅读更多]

Linux Kprobes介绍

2013年3月10日 2 条评论 15,201 次浏览

不得不说printk()函数是一个伟大的调试工具,它几乎能帮助我们在Linux系统的任何位置把我们想要的数据打印出来,但同时这也是一个比较耗时的过程,因为我们得把printk()函数加入到Linux内核源码的对应位置,重新编译模块,如果是内核修改,那么也许还得重启机器。 动态工具Kprobe的出现,使得我们可以在Linux系统的大部分地方都能通过更简便、快速的方法获得更多的内核信息,在前面文章里曾介绍的SystemTap也就是以Kprobe为基石而成的用户命令接口。 Kprobe,简单点说就是允许我们在内核执行到指定的地方时回调一个我们的自定义函数,在这个自定义函数内,我们就可以执行一些内核信息(类似于以前的printk())收集动作,完了之后内核再回去执行它原本的逻辑。 在这里,“内核执行到指定的地方”有一个专门的术语叫做probe(探针),目前有三种类型的probe,分别为: kprobes:内核代码的任何指令处。 jprobes:内核函数的入口处,因此可以很方便的获取到对应内核函数的参数。 kretprobes:内核函数的退出点。 先看个Kprobe实例: /* ... [阅读更多]

如何禁止Linux内核的-O2编译选项

2013年3月10日 2 条评论 18,498 次浏览

已有各种工具可以帮助我们调试内核,比如UML、kgdb、qemu等,但比较麻烦的是gdb经常给我一个“value optimized... [阅读更多]

Socket选项系列之SO_LINGER

2013年2月24日 7 条评论 26,459 次浏览

SO_LINGER是nginx里用到的另外一个重要套接口选项(因为它涉及到的问题很重要),虽然它不是特定于TCP套接口的,但针对的仍然是面向连接的协议,因此就TCP而言,自然也是可以使用它,这里就统一以TCP为例(即下面所提到的套接口仍然都还是TCP套接口)来进行阐述。 当应用程序在调用close()函数关闭TCP连接时,Linux内核的默认行为是将套接口发送队列里的原有数据(比如之前残留的数据)以及新加入的数据(比如函数close()产生的FIN标记,如果发送队列没有残留之前的数据,那么这个FIN标记将单独产生一个新数据包)发送出去并且销毁套接口(并非把相关资源全部释放,比如只是把内核对象sock标记为dead状态等,这样当函数close()返回后,TCP发送队列的数据包仍然可以继续由内核协议栈发送,但是一些相关操作就会受到影响和限制,比如对数据包发送失败后的重传次数)后立即返回。这需要知道两点:第一,当应用程序获得close()函数的返回值时,待发送的数据可能还处在Linux内核的TCP发送队列里,因为当我们调用write()函数成功写出数据时,仅表示这些数据被Linux内核接收放入到发送队列,如果此时立即调用close()函数返回后,那么刚才write()的数据限于TCP本身的拥塞控制机制(比如发送窗口、接收窗口等等),完全有可能还呆在TCP发送队列里而未被发送出去;当然也有可能发送出去一些,毕竟在调用函数close()时,进入到Linux内核后有一次数据包的主动发送机会,即: tcp_close()... [阅读更多]

Socket选项系列之TCP_DEFER_ACCEPT

2013年2月24日 1 条评论 11,038 次浏览

学过计算机网络课程的人,应该都知道TCP协议有个连接状态转换图,也许对其整体详细不甚清楚,但至少对TCP握手协议有些印象。标准的TCP三次握手(本节仅考虑这种情况,对于四次握手等其他情况,可以参考RFC... [阅读更多]

Socket选项系列之TCP_CORK

2013年2月24日 没有评论 9,538 次浏览

从上一节的内容可以看到,选项TCP_NODELAY是禁用Nagle算法,即数据包立即发送出去,而选项TCP_CORK与此相反,可以认为它是Nagle算法的进一步增强,即阻塞数据包发送,具体点说就是:TCP_CORK选项的功能类似于在发送数据管道出口处插入一个“塞子”,使得发送数据全部被阻塞,直到取消TCP_CORK选项(即拔去塞子)或被阻塞数据长度已超过MSS才将其发送出去。举个对比示例,比如收到接收端的ACK确认后,Nagle算法可以让当前待发送数据包发送出去,即便它的当前长度仍然不够一个MSS,但选项TCP_CORK则会要求继续等待,这在前面的tcp_nagle_check()函数分析时已提到这一点,即如果包数据长度小于当前MSS... [阅读更多]