扯淡,关于如何跟踪理解子系统

2017年1月31日 没有评论

cfs调度器在2007年就被造了出来,如果没有在那个时候正好跟上她,也就是说在2007年她刚出来的时候就有能力理解她,而是到了五年后,甚至七八年后才开始想要真正意义上的理解调度机制,没关系。不过可能要多花些时间,我还走在这样的路上。

就调度器而言,在某个非常革命性的patch出现时,可以以此切入,着重关注这个patch,因为会有很多牛人对这样的patch有讨论,这些讨论就是需要吸收的原料,而书已经不能用来理解系统了。此时即使猛地跟踪这样的patch还是不能整体很好的理解,因为,系统发展的时间太长了,此时应该要从她被造出来开始切入,然后对一切的有关patch及讨论进行理解,这只能默默地,也可以参与到别人的讨论,越默默,越到位,反复,实验(我忽略)。哪天就可以不知不觉地进入那个领地,哈,纯属扯淡,新年快乐…

分类: 内核 标签:

好起来,保重

2016年2月29日 1 条评论

一直没有在此地记录一些东西。打算慢慢在此share一些自己对内核社区(尤其是调度器方面)的追踪。也许不会涉及很多细节,也许由于自己实验能力的缺陷不能给予说明,不过,尽量share一些所得的资料等等。

说到调度器,其实上层应用多线程等等会用到相似的思维方式,就是并发。这是真正理解内核的一种比较关键的技术。目前我能了解的关于内核并发的资料是有Paul E.McKenney在2015年汇编成的一本书籍:《Is Parallel Programming Hard, And, If So, What Can You Do About It?》。目前我也在study。

通过这一段时间对scheduler mail list的追踪和学习,发现他们的贡献者大多数是cpu厂商和服务器等的开发者,有来自德国,荷兰,英国,比利时,韩国,当然还有我们中国。目前我了解的国内在scheduler中活跃的开发者有Michael Wang(IBM), Alex Shi(Intel)等。

好,不多说。最后有一个希望:好起来,保重。一切都在变,一切又都在stay。

分类: 内核 标签:

gdb调试coredump文件函数名总是一堆问号的问题

2014年9月17日 没有评论

问题描述:已经在编译选项中加入了-g,但是查看coredump文件时,函数名还是一堆问号,使用的命令为:gdb -c core

解决方案:由于gdb -c core这样的使用在有些系统下支持不是很好,所以推荐用如下两种方法:

1)gdb exe
(gdb) core-file core

2)gdb -c core
(gdb) file exe

而其中第二种方法在某些系统上也是不好用的,所以就用第一种即可。

分类: gdb 标签:

【转】打开tcp_tw_recycle引起的一个问题

2014年7月13日 没有评论

今天普空说了一个问题就是如果设置了tcp_tw_recycle ,那么如果客户端是NAT出来的,那么就可能会出现连接被直接rst的情况。然后我google了下,在内核列表也有人说了这个问题 https://lkml.org/lkml/2008/11/15/67

The big problem is that both are incompatible with NAT. So if you
ever talk to any NATed clients don’t use it.


源码之前了无秘密,我们来看代码,为什么会出现这种问题,我这里是3.4.4的内核。核心代码是在tcp_v4_conn_request中,这个函数是什么时候被调用呢,是当listen socket收到syn包的时候被调用。直接来看涉及到tw_recycle的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
#define TCP_PAWS_MSL    60      /* Per-host timestamps are invalidated
                     * after this time. It should be equal
                     * (or greater than) TCP_TIMEWAIT_LEN
                     * to provide reliability equal to one
                     * provided by timewait state.
                     */
#define TCP_PAWS_WINDOW 1       /* Replay window for per-host
                     * timestamps. It must be less than
                     * minimal timewait lifetime.
        /* VJ's idea. We save last timestamp seen
         * from the destination in peer table, when entering
         * state TIME-WAIT, and check against it before
         * accepting new connection request.
         *
         * If "isn" is not zero, this request hit alive
         * timewait bucket, so that all the necessary checks
         * are made in the function processing timewait state.
         */
        if (tmp_opt.saw_tstamp &&
            tcp_death_row.sysctl_tw_recycle &&
            (dst = inet_csk_route_req(sk, &fl4, req)) != NULL &&
            fl4.daddr == saddr &&
            (peer = rt_get_peer((struct rtable *)dst, fl4.daddr)) != NULL) {
            inet_peer_refcheck(peer);
            if ((u32)get_seconds() - peer->tcp_ts_stamp < TCP_PAWS_MSL &&
                (s32)(peer->tcp_ts - req->ts_recent) >
                            TCP_PAWS_WINDOW) {
                NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_PAWSPASSIVEREJECTED);
                goto drop_and_release;
            }
        }

可以看到当满足下面所有的条件时,这个syn包将会被丢弃,然后释放相关内存,并发送rst。
1 tcp的option有 time stamp字段.
2 tcp_tw_recycle有设置。
3 在路由表中是否存在完全相同的流(如果打开了xfrm的话,还要比较端口,默认xfrm应该是打开的),如果存在则直接返回.
4 并且数据包的源地址和新请求的源地址相同.
5 根据路由表以及源地址能够查找到保存的peer(这个可以看我以前的blog,也就是保存了一些连接统计信息).
6 当前时间(接收到syn)比最后一次的时间(time stamp)小于60秒.
7 已经存在peer的最近一次时间戳要大于当前请求进来的时间戳.

从上面可以看到,上面的条件中1/2都是 server端可以控制的,而其他的条件,都是很容易就满足的,因此我们举个例子。

如果客户端是NAT出来的,并且我们server端有打开tcp_tw_recycle ,并且time stamp也没有关闭,那么假设第一个连接进来,然后关闭,此时这个句柄处于time wait状态,然后很快(小于60秒)又一个客户端(相同的源地址,如果打开了xfrm还要相同的端口号)发一个syn包,此时linux内核就会认为这个数据包异常的,因此就会丢掉这个包,并发送rst。

而现在大部分的客户端都是NAT出来的,因此建议tw_recycle还是关闭,或者说server段关闭掉time stamp(/proc/sys/net/ipv4/tcp_timestamps).

分类: 内核 标签:

【转】jQuery诞生记-原理与机制

2014年4月20日 没有评论

by zhangxinxu from http://www.zhangxinxu.com
本文地址:http://www.zhangxinxu.com/wordpress/?p=3520

一、看似偶然的东西实际是必然会发生的

我大学时候在图书馆翻过一本很破旧的书,讲生物理论的,主要内容就是探讨生命的产生是偶然还是必然。里面很多亚里士多德都看不懂的公式计算什么的,还有模拟原始地球环境出现了有机物的实验什么的 。总之,书论述的观点是:“在当时的地球环境下,生命的产生是必然的!” 无数次机会的偶然条件、无数次化合物的相遇反应等必定会产生有机物,再有N多偶然,有机物必然形成了有机体……

这种理论类似于,你是个过马路非常小心的人,且你万寿无疆,除了怕被汽车撞。给你100万年的寿命,你最后必然还是被车撞死。

如果以这种理论来看jQuery的出现,结论也应该是必然的

二、需求、动力、发展、事物产生与jQuery的诞生

刀削面机器人

一个成熟的东西显然不是一口气就出来的,所谓“一铲子挖不了一口井”,我想jQuery的原作者再天才,也是循序渐进过来的,如何个循序渐进法,我想,很有可能就是需求驱动而产生的,好比上图刀削面机器人,据说现在已经第八代了!

1. gelElementById太长了
页面上有个按钮,还有个图片,我想点击按钮图片隐藏,如下HTML:

<button id="button">点击我</button>
<img id="image" src="xxx.jpg">

于是,我的脚本可能就这样:

var button = document.getElementById("button")
    , image = document.getElementById("image")

button.onclick = function() {
    image.style.display = "none";
};

有何问题?人几乎都是天生的“懒惰者”,document.getElementById名称长且重复出现,好像到了公司发现卡没带又回家重新拿卡的感觉,我希望越简单越好。恩, 我很喜欢钱,$这个符号我很喜欢,我打算改造一番,简化我的工作:

var $ = function(id) {
    return document.getElementById(id);
};

<span style="color: #cd0000;">$("button")</span>.onclick = function() {
    <span style="color: #cd0000;">$("image")</span>.style.display = "none";
};

这里的$()就是最简单的包装器,只是返回的是原生的DOM对象。

2. 我需要一个简洁的暗号,就像“芝麻开门”
后来页面复杂了,点击一个按钮,有2张图片要隐藏。

$("button").onclick = function() {
    $("image1").style.display = "none";
    $("image2").style.display = "none";
};

好像又看见长长的重复的东西,xxx.style.display = "none", 为什么每次开门都要从包里找到钥匙、对准插口插进去、还要左扭扭右扭扭呢?一次还好,天天经常老是这样怎受得了。设想,要是有个芝麻开门的暗号就好了,“open开”,声音识别,门自动开了,多省心。

这里每次隐藏都要xxx.style.display = "none", 比每天拿钥匙开门还要烦,我希望有一个快捷的方式,例如,“hide隐”,语句识别,元素自动隐藏,多省心。

就是要变成下面的效果:

$("button").onclick = function() {
    $("image1")<span style="color: #cd0000;">.hide()</span>;
    $("image2")<span style="color: #cd0000;">.hide()</span>;
};

3. 如何识别“芝麻开门”的暗号
$("image1")本质是个DOM元素,$("image1").hide()也就是在DOM元素上扩展一个hide方法,调用即隐藏。

哦,扩展,立马想到了JS中的prototype原型。//zxx: 老板,现在满大街什么菜最便宜。老板:原型啊,都泛滥了!

<span style="color: #cd0000;">HTMLElement.prototype</span>.hide = function() {
    this.style.display = "none";
};

上面代码的demo地址应该不会被人看到吧……

虽然在身体上钻了个窟窿插进入了一个方法,毕竟浏览器有有效果啊,切肤之痛就不算什么了。但是,我们是在泱泱天朝,很多IE6~IE8老顽固,这些老东西不认识HTMLElement,对于HTMLElement自残扩展之法根本理解不了,而这些老家伙掌管了半壁江山。唉,面对现实,元素直接扩展是行不通了。

因此,由于兼容性,我们需要想其他扩展方法。

4. 条条大路通罗马,此处不留爷,自有留爷处
虽IE6~IE8不识HTMLElement原型扩展,但是,Function的原型扩展其认识啊。管不管用,暂时不得而知,先随便搞个简单的试试呗~

var F = function() {};
F.prototype.hide = function() {
    this?.style.display = "none";
};

new F().hide();    <span style="color: green;">// 这个实现隐藏?</span>

本文至少还有一半的内容,但是,全文的最难点就在这里的,对new F()的认识和理解。

上面的代码,new F()您可以看做是this?.style这里的this. 您可能会跳起来抢答道:“那new F()return值 = DOM元素不就完事OK啦!—— this.style.hide = new F().style.hide = DOM.style.hide”!

很傻很天真

只要new表达式之后的constructor返回(return)一个引用对象(数组,对象,函数等),都将覆盖new创建的匿名对象,如果返回(return)一个原始类型(无return时其实为return原始类型undefined),那么就返回new创建的匿名对象。

上面的引用来自这里。什么意思呢?说白了就是,new F()如果没有返回值(Undefined类型),或返回值是5种基本型(Undefined类型、Null类型、Boolean类型、Number类型、String类型)之一,则new F()我们可以看成是原型扩展方法中的this; 如果返回是是数组啊、对象啊什么的,则返回值就是这些对象本身,此时new F() ≠ this

举例说明:

var F = function(id) {
    return document.getElementById(id);
};

new F("image1") == document.getElementById("image1");    <span style="color: green;">// true 说明看上去返回DOM对象,实际确实就是DOM对象</span>
var F = function(id) {
    return id;
};

new F("image1") == "image1";    <span style="color: green;">// false 说明看上去返回字符串值,实际并不是字符串</span>

回到上面天真的想法。要想使用prototype.hide方法中的this, 偶们就不能让F函数有乱七八糟的返回值。

因此,new F()直接返回DOM是不可取的,但我们可以借助this间接调用。比方说:

var F = function(id) {
    <span style="color: #cd0000;">this.element</span> = document.getElementById(id);
};
F.prototype.hide = function() {
    <span style="color: #cd0000;">this.element.style.display</span> = "none";
};

new F("image").hide();    <span style="color: green;">// 看你还不隐藏</span>

上面代码的demo地址应该不会被人看到吧……

5. 我不喜欢太暴露
F()中的this.element实际上将element这个属性直接暴露在了new F("image")上!

new F("image").hasOwnProperty("element");    <span style="color: green;">// true</span>

直接暴露的属性

太暴露了,我不喜欢~~
太暴露

如何隐藏?代码如下:

var F = function(id) {
    return this.getElementById(id);
};
F.prototype.getElementById = function(id) {
    <span style="color: #cd0000;">this.element</span> = document.getElementById(id);
    return <span style="color: #cd0000;">this</span>;
};
F.prototype.hide = function() {
    this.element.style.display = "none";
};

new F("image").hide();    <span style="color: green;">// 看你还不隐藏</span>

元素获取方法放在prototype上,通过F()执行。你可能会奇怪了,你刚明明说“new F()直接返回DOM是不可取的”,怎么现在又有return呢?大家务必擦亮眼睛,F.prototype.getElementById的返回值是this,也就是new F()的返回值是this. 形象点就是new F("image")出了一拳,又反弹到自己脸上了。

于是乎,现在就没有直接暴露的API了。

上面代码的demo地址应该不会被人看到吧……

6. 我不喜欢new, 我喜欢$
new F("image")这种写法我好不喜欢,我喜欢$, 我就是喜欢$, 我要换掉。

好吧,把new什么什么藏在$方法中把~

var $ = function(id) {
    return new F(id);
};

于是,上面的图片隐藏的直接执行代码就是:

$("image").hide();

上面代码的demo地址应该不会被人看到吧……

IE6浏览器也是支持的哦!是不是已经有些jQuery的样子啦!

7. 你怎么就一种姿势啊,人家都腻了诶
循序渐进到现在,都是拿id来举例的,实际应用,我们可能要使用类名啊,标签名啊什么的,现在,为了接下来的继续,有必要支持多个“姿势”。

在IE8+浏览器中,我们有选择器API,document.querySelectordocument.querySelectorAll,前者返回唯一Node,后者为NodeList集合。大统一起见,我们使用后者。于是,就有:

var F = function(selector, context) {
    return this.getNodeList(selector, context);
};
F.prototype.getNodeList = function(selector, context) {
    context = context || document;
    this.element = context.<span style="color: #cd0000;">querySelectorAll</span>(selector);
    return this;
};

var $ = function(selector, context) {
    return new F(selector, context);
};

此时,我们就可以使用各种选择器了,例如,$("body #image")this.element就是选择的元素们。

8. IE6/IE7肿么办?
IE6/IE7不认识querySelectorAll,咋办?
怎么办?

jQuery就使用了一个比较强大的选择器框架-Sizzle. 知道就好,重在演示原理,因此,下面还是使用原生的选择器API示意,故demo效果需要IE8+浏览器下查看。

8. 遍历是个麻烦事
this.element此时类型是NodeList, 因此,直接this.element.style.xxx的做法一定是报错,看来有必要循环下:

F.prototype.hide = function() {
    var i=0, length = this.element.length;
    for (; i<length; i+=1) {
        this.element[i].style.display = "none";
    }    
};

于是乎:

$("img").hide();  <span style="color: green;">// 页面所有图片都隐藏啦!</span>

上面代码的demo地址应该不会被人看到吧……

单纯一个hide方法还可以应付,再来个show方法,岂不是还要循环遍历一次,岂不是要烦死~ 

因此,急需一个遍历包装器元素的方法,姑且叫做each吧~

于是有:

F.prototype<span style="color: #cd0000;">.each</span> = function(fn) {
    var i=0, length = this.element.length;
    for (; i&lt;length; i+=1) {
        fn.call(this.element[i], i, this.element[i]);
    }
    return this;
};
F.prototype.hide = function() {
    this<span style="color: #cd0000;">.each</span>(function() {
       this.style.display = "none";
    });
};

$("img").hide();  <span style="color: green;">// 页面所有图片都隐藏啦!</span>

上面代码的demo地址应该不会被人看到吧……

9. 我不喜欢this.element, 可以去掉吗?
现在包装器对象结构类似这样:

F.prototype = {
    element: [NodeList],
    each: function() {},
    hide: function() {}
}

element看上去好碍眼,就不能去掉吗?可以啊,宝贝,NodeList是个类数组结构,我们把它以数值索引形式分配到对象中就好啦!一来去除冗余element属性,二来让原型对象成为类数组结构,可以有一些特殊的功能。

于是,F.prototype.getNodeList需要换一个名字了,比方说初始化init, 于是有:

F.prototype<span style="color: #cd0000;">.init</span> = function(selector, context) {
    var nodeList = (context || document).querySelectorAll(selector);
    <span style="color: #cd0000;">this.length</span> = nodeList.length;
    for (var i=0; i&lt;this.length; i+=1) {
        <span style="color: #cd0000;">this[i]</span> = nodeList[i];    
    }
    return this;
};

此时,each方法中,就没有烦人碍眼的this.element[i]出现了,而是直接的this[i].

F.prototype.each = function(fn) {
    var i=0, length = <span style="color: #cd0000;">this.length</span>;
    for (; i&lt;length; i+=1) {
        fn.call(<span style="color: #cd0000;">this[i]</span>, i, <span style="color: #cd0000;">this[i]</span>);
    }
    return this;
};

我们也可以直接使用索引访问包装器中的DOM元素。例如:$("img")[0]就是第一张图片啦!

上面代码的demo地址应该不会被人看到吧……

10. 我是完美主义者,我特不喜欢F名称,可以换掉吗?
F这个名称从头到尾出现,我好不喜欢的来,我要换成$, 我就是要换成$符号……

这个……$已经用了啊,再用冲突的吧。再说,你又不是狐后,耍无赖也没用啊……

狐后耍无赖

御姐发飙了

 好吧,想想其他办法吧。一步一步来,那我把所有的F换成$.fn.

就有:
所有的F换成$.fn之后~

上图代码的demo地址应该不会被人看到吧……

显然,运行是OK的。似乎也非常有jQuery的模样了,但是,实际上,跟jQuery比还是有差别的,有个较大的差别。如果是上图代码所示的JS结构,则包装器对象要扩展新方法,每个都需要再写一个原型的。例如,扩展一个attr方法,则要写成:

$.fn.prototype.attr = function() {
    <span style="color: green;">// ...</span>
};

又看到prototype了,高级的东西应该要隐藏住,否则会给人难以上手的感觉。那该怎么办呢?御姐不是好惹的。

脑子动一下就知道了,把F.prototype换成$.fn不久好了。这样,扩展新方法的时候,直接就是

$.fn.attr = function() {
    <span style="color: green;">// ...</span>
};

至此,就使用上讲,与jQuery非常接近了。 但是,还有几个F怎么办呢,总不能就像下面这样放着吧:

var $ = function(selector, context) {
    return new F(selector, context);
};
var F = function(selector, context) {
    return this.init(selector, context);
};

$.fn = F.prototype;

$.fn.init = function(selector, context) {
    <span style="color: green;">// ...</span>
    return this;
};
$.fn.each = function(fn) {
   <span style="color: green;">// ...</span>
};
$.fn.hide = function() {
   <span style="color: green;">// ...</span>
};

数学中,我们都学过合并同类项。仔细观察上面的的代码:
$()返回的是new F(),而new F()又是返回的对象的引用。擦,这返回来返回去的,参数又是一样的,我们是不是可以一次性返回,然后再做些手脚,让$.fn.init返回的this依然能够正确指向。

于是,一番调整有:

var $ = function(selector, context) {
    return new $.fn.init(selector, context);
};
var F = function() { };

$.fn = F.prototype;
$.fn.init = function(selector, context) {
    <span style="color: green;">// ...</span>
    return this;
};

<span style="color: green;">// ...</span>

上面代码显然是有问题的,new的是$.fn.init$.fn.init的返回值是this. 也就是$()的返回值是$.fn.init的原型对象,尼玛$.fn.initprototype原型现在就是个光杆司令啊,哟,正好,$.fn对应的原型方法,除了init没用外,其他hide()each()就是我们需要的。因此,我们需要加上这么一行:

$.fn.init.prototype = $.fn

于是,$()的返回值从$.fn.init.prototype一下子变成$.fn,正好就是我们一开始的扩展方法。

于是乎,大功告成。慢着……
慢着……

上面明明还有残留的F呢!

 哦,那个啊。F是任意函数,$本身就是函数,因此,直接使用$替换就可以了:

var $ = function(selector, context) {
    return new $.fn.init(selector, context);
};
<del datetime="2013-07-17T03:12:44+00:00">var F = function() { };</del>   // 这个直接删除
$.fn = <span style="color: #cd0000;">$</span>.prototype;
$.fn.init = function(selector, context) {
    <span style="color: green;">// ...</span>
    return this;
};

<span style="color: green;">// ...</span>

上图代码的demo地址应该不会被人看到吧……

实际上,如果你不是非得一个$行便天下的话,到了上面进阶第9步就足够了。jQuery在第10步的处理是为了彰显其$用得如此的出神入化,代码完美,令人惊叹!

至此,jQuery大核心已经一步一步走完了,可以看到,所有的这些进阶都是根据需求、实际开发需要来的,慢慢完善,慢慢扩充的!

11. 每个扩展方法都要$.fn.xxxx, 好闹心的来

$.fn.css = function() {}
$.fn.attr = function() {}
$.fn.data = function() {}
<span style="color: green;">// ...</span>

每个扩展前面都有个$.fn, 好讨厌的感觉,就不能合并吗?

于是,jQuery搞了个extend方法。

$.fn.extend({
    css: function() {},
    attr: function() {},
    data: function() {},
    <span style="color: green;">// ...</span>
});

12. $()不仅可以是选择器字符串,还可以是DOM
init方法中,判断第一个参数,如果是节点,直接this[0] = this_node. over!

以下13~?都是完善啊,补充啊,兼容性处理啊什么的,没有价值,到此为止!

三、排了很长队的结束语

网上也有其他一些介绍jQuery原理或机制的文章,可能当事人自己理解,而阅读者本来就不懂,说来说去,越说越绕,可能更不懂了。

jQuery是很优秀,好比身为灵长类的人类。但是,其诞生显然是从简单开始的。因此,要了解人类,可以通过追溯其起源。如果你是上帝,要让你造一个人,你会怎么造,是一口气出来?女娲造人还要捏泥人呢!不妨从单细胞生物开始,随着自然进化,淘汰,自然而然,就会出现人类,上帝他就是这么干的。

jQuery的诞生也大致如此,要想了解jQuery,可以试试踏着本文jQuery的成长足迹,一点一点逐步深入,您就会了解为何jQuery要这么设计,它是如何设计的等。

虽然,内容由浅及深,但是,其中涉及的原型以及new构造函数的一些特性,对于新人而言,还是有一些理解门槛的,希望我的描述与解释可以让你有一丝豁然开朗,那就再好不过了。

感谢您的阅读至此,欢迎指出文章可能书写不准确的地方,再次感谢!

月底在百姓网有个小分享,演示文档连个肉渣子还没准备呢。因此,未来一周休文。

原创文章,转载请注明来自张鑫旭-鑫空间-鑫生活[http://www.zhangxinxu.com]
本文地址:http://www.zhangxinxu.com/wordpress/?p=3520

(本篇完)

分类: Javascript 标签: