├── .gitignore ├── Makefile ├── README.md ├── chinanet ├── 2012121720233592.png ├── chinanet.html ├── chinanet.md ├── full.html ├── full.md ├── google.png ├── newsbaidu.png └── python.png ├── context ├── PyConChina2014-stick-logo.png ├── context.html ├── context.md ├── context_slide.html ├── context_slide.md ├── context_test.html ├── context_test.md ├── g_chan.png ├── g_chan.txt ├── g_lock.png ├── g_lock.txt ├── g_sched.png ├── g_sched.txt ├── py_yieldfrom.png ├── py_yieldfrom.txt ├── t_lock.png ├── t_lock.txt ├── t_yield.png └── t_yield.txt ├── ipynb ├── learn-python-qrcode.jpg ├── life.csv ├── population.csv ├── pycon2016.ipynb ├── pycon2016.md ├── pycon2016.slides.html ├── pycon2019.ipynb ├── pycon2019.md ├── pycon2019.slides.html ├── pycon2024.ipynb ├── pycon2024.slides.html └── wechat-qrcode.png ├── md ├── apt.html ├── apt.md ├── docker.html ├── docker.md ├── docker1.html ├── docker1.md ├── kernel-read.html ├── kernel-read.md ├── lic.html ├── lic.md ├── lic_tip.html ├── lic_tip.md ├── linuxsec_basic.md ├── meta.html ├── meta.md ├── passwd.html ├── passwd.md ├── python-startup.html ├── python-startup.md ├── qiniu_deepin.html ├── qiniu_deepin.md ├── schemepy.html ├── schemepy.md ├── why_python.html └── why_python.md ├── pdf ├── GFW.pdf ├── elisp.pdf ├── linux_fs.pdf ├── linux_on_net.pdf ├── one_way_cross_gfw.pdf ├── python_source.odp ├── python_source.pdf ├── step into cryptography.odp └── step into cryptography.pdf └── rir ├── cli.md ├── hwinfo.rec ├── noapt.rec ├── pycon.html ├── pycon.md └── sync.rec /.gitignore: -------------------------------------------------------------------------------- 1 | README.html -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | ### Makefile --- 2 | 3 | ## Author: shell@dsk.lan 4 | ## Version: $Id: Makefile,v 0.0 2014/07/16 13:44:42 shell Exp $ 5 | ## Keywords: 6 | ## X-URL: 7 | 8 | build: 9 | find . -name '*.md' -exec ~/bin/md2slide {} \; 10 | 11 | clean: 12 | find . -name '*.html' -delete 13 | 14 | ### Makefile ends here 15 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Directory 2 | 3 | * [elisp的简单介绍](https://raw.githubusercontent.com/shell909090/slides/master/pdf/elisp.pdf) 2010-12在SHLUG的分享 4 | * [Python源码解析读书笔记](https://raw.githubusercontent.com/shell909090/slides/master/pdf/python_source.pdf) ([odp](https://raw.githubusercontent.com/shell909090/slides/master/pdf/python_source.odp)) 2011-03-27在五分钟的分享 5 | * [linux的文件系统对比](https://raw.githubusercontent.com/shell909090/slides/master/pdf/linux_fs.pdf) 2011-08-21在SHLUG的分享 6 | * [实战Linux网络部署](https://raw.githubusercontent.com/shell909090/slides/master/pdf/linux_on_net.pdf) 2011-11-06在东华大学SHLUG的2011年年度技术分享会的演讲 7 | * [元编程在redis orm中的应用](http://shell909090.github.com/slides/md/meta.html) ([source](https://github.com/shell909090/slides/blob/master/md/meta.md)) 2012-10-20在PyCon2012的演讲 8 | * [why python](http://shell909090.github.com/slides/md/why_python.html) ([source](https://github.com/shell909090/slides/blob/master/md/why_python.md)) 2013-04-17在GDG Meetup – Python Salon上的分享 9 | * [python startup](http://shell909090.github.com/slides/md/python-startup.html) ([source](https://github.com/shell909090/slides/blob/master/md/python-startup.md)) 2013-03-23在上海浦东软件园,开源力量公开课上的分享 10 | * [schemepy](http://shell909090.github.com/slides/md/schemepy.html) ([source](https://github.com/shell909090/slides/blob/master/md/schemepy.md)) 2013-05-25在shlug聚会上的分享 11 | * [大陆地区网络状况简介](http://shell909090.github.com/slides/chinanet/chinanet.html) ([source](https://github.com/shell909090/slides/blob/master/chinanet/chinanet.md)) 2013-08-03在COSCUP上的演讲 12 | * [深入介绍七牛云存储](http://shell909090.github.com/slides/md/qiniu_deepin.html) ([source](https://github.com/shell909090/slides/blob/master/md/qiniu_deepin.md)) 2013-09在linux deepin上的演讲 13 | * [密码强度分析](http://shell909090.github.com/slides/md/passwd.html) ([source](https://github.com/shell909090/slides/blob/master/md/passwd.md)) 2014-03在SHLUG的演讲 14 | * [docker不是虚拟机](http://shell909090.github.com/slides/md/docker.html) ([source](https://github.com/shell909090/slides/blob/master/md/docker.md)) 2014-06-29在云计算和大数据,2014-07-27在SHLUG的演讲 15 | * [开源许可协议漫说](http://shell909090.github.com/slides/md/lic.html) ([source](https://github.com/shell909090/slides/blob/master/md/lic.md)) ([tip](https://github.com/shell909090/slides/blob/master/md/lic_tip.md)) 2014-07-25在华东师范大学2014年暑期学校培训的演讲 16 | * [简说apt包管理系统](http://shell909090.github.com/slides/md/apt.html) ([source](https://github.com/shell909090/slides/blob/master/md/apt.md)) 2014-08-28在七牛的培训 17 | * [上下文切换技术简介](http://shell909090.github.com/slides/context/context_slide.html) ([source](https://github.com/shell909090/slides/blob/master/context/context_slide.md)) ([tip](https://github.com/shell909090/slides/blob/master/context/context.md)) ([test](https://github.com/shell909090/slides/blob/master/context/context_test.md)) 2014-11-14在七牛的培训,2014-11-15在PyCon2014上的演讲 18 | * [docker的功能,结构,用法](http://shell909090.github.com/slides/md/docker1.html) ([source](https://github.com/shell909090/slides/blob/master/md/docker1.md)) 2015-07-25在mpd大会上的演讲 19 | * [run it remote 一种远程执行代码的方法](http://shell909090.github.com/slides/rir/pycon.html) 2015-09-12在PyCon2015 Shanghai的演讲 20 | * [cli版本](https://github.com/shell909090/slides/blob/master/rir/cli.md) 播放工具[MDP](https://github.com/visit1985/mdp)。 21 | * [hwinfo录像](http://shell909090.github.com/slides/rir/hwinfo.rec) 22 | * [noapt录像](http://shell909090.github.com/slides/rir/noapt.rec) 23 | * [sync录像](http://shell909090.github.com/slides/rir/sync.rec) 24 | * [linux system security and tunning - basic](https://github.com/shell909090/slides/blob/master/md/linuxsec_basic.md) 播放工具[MDP](https://github.com/visit1985/mdp)。 25 | * [程序猿训练师养成计划1.0](http://shell909090.github.com/slides/ipynb/pycon2016.slides.html) [源文件](https://github.com/shell909090/slides/blob/master/ipynb/pycon2016.ipynb) 2016-09-10在PyCon2016 Shanghai的演讲 26 | * [step into cryptography](https://github.com/shell909090/slides/raw/master/pdf/step%20into%20cryptography.pdf) ([odp](https://github.com/shell909090/slides/raw/master/pdf/step%20into%20cryptography.odp)) 2018-05-29在饿了么的演讲 27 | * [在github上阅读linux源码](https://shell909090.github.com/slides/md/kernel-read.html) ([source](https://github.com/shell909090/slides/raw/master/md/kernel-read.md)) 2019-08-01在SHLUG月会上的演讲 28 | * [home bot](https://shell909090.github.com/slides/ipynb/pycon2019.slides.html) [源文件](https://github.com/shell909090/slides/blob/master/ipynb/pycon2019.ipynb) 2019-10-19在PyCon2019 HangZhou的演讲 29 | * [pandas计算人口和劳动力数据](https://shell909090.github.io/slides/ipynb/pycon2024.slides.html) [源文件](https://github.com/shell909090/slides/blob/master/ipynb/pycon2024.ipynb) 30 | 31 | # LICENSE 32 | 33 | 未特别说明,一律[cc-by-sa3.0](https://creativecommons.org/licenses/by-sa/3.0/us/)。 34 | -------------------------------------------------------------------------------- /chinanet/2012121720233592.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shell909090/slides/d8aa123521daedee78e85e06f80c621929afaebd/chinanet/2012121720233592.png -------------------------------------------------------------------------------- /chinanet/chinanet.md: -------------------------------------------------------------------------------- 1 | # 上网的选择 # 2 | 3 | * 电信 4 | * 教育网 5 | * 网通 6 | * 联通 7 | * 铁通 8 | * 有线通 9 | * 其他 10 | 11 | ## 现象 ## 12 | 13 | 1. 有的时候打网游总是掉线。 14 | 2. 有些网页也偶尔会打不开。 15 | 3. 有时下载总是有点奇奇怪怪的毛病。 16 | 4. 在电信没事。 17 | 18 | ## 测试1 ## 19 | 20 | 下面是同一个机房同一个机架上的网通和电信服务器,从电信用户处ping的测试结果。 21 | 22 | 电信 23 | 24 | 26 packets transmitted, 26 received, 0% packet loss, time 25038ms 25 | rtt min/avg/max/mdev = 4.292/5.536/8.446/0.859 ms 26 | 27 | 网通 28 | 29 | 21 packets transmitted, 21 received, 0% packet loss, time 20028ms 30 | rtt min/avg/max/mdev = 129.111/132.009/134.310/1.234 ms 31 | 32 | 作为对比,这是同一用户mtr niconico在日本的服务器。 33 | 34 | niconico in jp 35 | 36 | HOST: e135 Loss% Snt Last Avg Best Wrst StDev 37 | 16.|-- 203.192.149.78 0.0% 10 127.1 127.2 125.2 129.7 1.4 38 | 39 | ## 原因 ## 40 | 41 | 在大陆地区,跨越全国的核心主干网有很多。但是公开主干网络只有两三家,电信,网通,教育网。其余都是些比较小的运营商。 42 | 43 | 根据电信拆分方案,早些年电信所拥有的网络被拆分成两家。南方的归电信,北方的划入网通。电信当然不愿意有那么多竞争者,但是要连不通问题更大。于是,在非公开的记录下,电信做了一点小小的调整。 44 | 45 | 电信把通往其他ISP的数据报文优先级调整到最低,比出国还低。 46 | 47 | 所以如果在夜深人静没人用网络的时候,两者的速度就没什么太大区别了。。。 48 | 49 | 这就是大陆网络第一怪——双线问题。 50 | 51 | ## 测试2 ## 52 | 53 | 我们测试一下路由,看看哪里是慢的关键。 54 | 55 | 电信 56 | 57 | HOST: e135 Loss% Snt Last Avg Best Wrst StDev 58 | 1.|-- 192.168.1.1 0.0% 10 1.5 1.8 1.5 2.6 0.3 59 | 2.|-- 124.74.56.20 0.0% 10 5.9 4.7 2.8 13.8 3.3 60 | 3.|-- 124.74.57.225 0.0% 10 4.1 5.9 3.4 15.6 3.7 61 | 4.|-- 124.74.209.5 0.0% 10 4.0 5.7 3.8 14.7 3.4 62 | 5.|-- 61.152.80.10 0.0% 10 13.9 15.2 5.7 32.9 7.0 63 | 6.|-- 222.73.53.86 0.0% 10 8.7 7.7 4.1 13.7 3.2 64 | 7.|-- 222.73.241.18 0.0% 10 19.6 8.0 5.8 19.6 4.1 65 | 8.|-- 114.80.227.14 0.0% 10 150.3 149.2 143.1 160.9 5.7 66 | 9.|-- 114.80.227.153 0.0% 10 5.0 5.9 4.6 7.7 1.0 67 | 68 | --- 69 | 70 | 网通 71 | 72 | HOST: e135 Loss% Snt Last Avg Best Wrst StDev 73 | 1.|-- 192.168.1.1 20.0% 10 1.9 1.8 1.5 2.1 0.3 74 | 2.|-- 124.74.56.20 0.0% 10 3.7 4.1 3.2 8.1 1.4 75 | 3.|-- 124.74.57.229 0.0% 10 3.6 4.4 3.4 5.6 0.7 76 | 4.|-- 124.74.215.53 0.0% 10 4.2 5.5 4.2 13.9 3.0 77 | 5.|-- 202.101.63.138 0.0% 10 5.0 5.2 4.5 5.7 0.4 78 | 6.|-- 202.97.48.26 0.0% 10 7.8 7.5 5.0 11.4 1.8 79 | 7.|-- 202.97.15.226 0.0% 10 126.8 130.0 126.3 138.6 3.6 80 | 8.|-- 219.158.96.117 0.0% 10 131.2 133.9 128.6 159.1 8.9 81 | 9.|-- 219.158.98.146 30.0% 10 146.0 137.4 134.7 146.0 4.2 82 | 10.|-- 112.64.243.82 60.0% 10 133.7 136.2 131.8 140.4 4.1 83 | 11.|-- 112.64.252.210 0.0% 10 140.7 135.8 133.8 140.7 2.2 84 | 12.|-- 112.65.224.98 10.0% 10 218.2 208.2 152.1 331.1 59.7 85 | 13.|-- 112.65.229.2 10.0% 10 130.8 131.1 129.8 133.3 1.0 86 | 87 | --- 88 | 89 | 我们whois一下每个节点。注意到电信到网通最慢的点,刚好在电信的骨干网内部最后一跳上,而不是电信路由器到网通路由器之间。 90 | 91 | 202.97.48.26 92 | 93 | 202.97.15.226 94 | 95 | inetnum: 202.97.0.0 - 202.97.31.255 96 | netname: CHINANET-BB 97 | descr: CHINANET backbone network 98 | 99 | 实际上这倒是不足为奇。电信到google在美国机房的mtr表明,出国光纤延迟还没有电信最后一跳延迟大。当然,这个最后一跳正在做什么就不足为外人道了。 100 | 101 | ## 如果你是网站的工程师 ## 102 | 103 | * 双线双IP 104 | * BGP 105 | 106 | 但是价格。。。 107 | 108 | * 电信:100RMB/Mbps/mo 470NTD/Mbps/mo 109 | * BGP:300RMB/Mbps/mo 1410NTD/Mbps/mo 110 | 111 | 如果机器比较多,在电信和网通分别放一个机器比一个BGP机器更加便宜。所以开始我才举有线通的例子。因为网通用户也很多,很多站点为了网通也做了优化,所以有的时候网通用户并不能察觉问题。相反,有线通用户更少,很多网站不给有线通做优化。 112 | 113 | 但是有线通还得活,所以他们出台了另一个神奇的东西。 114 | 115 | # 运营商缓存 # 116 | 117 | ## 现象 ## 118 | 119 | V2EX的控诉帖: 120 | 121 | 1. 当这名用户在有线通去下载淘宝最新的程序时,下载到的永远是旧的。 122 | 2. 电信的下载到的则是最新的。 123 | 124 | ## 原理 ## 125 | 126 | 1. 大部分网站都是电信或网通的。电信和网通的访问都慢,用户觉得慢就会跑。 127 | 2. 有线通要生存,他们想花钱解决这个问题无法解决。为了解决这个问题,有线通对大文件下载都做了ISP级transparent proxy。 128 | 3. 当你下载一个文件的时候,拿到的是代理上的版本。这只消耗你到有线通运营商之间的流量,而不影响电信出口带宽。 129 | 4. 很多网站的Modify信息很有问题,cache参数要么没指定,要么胡乱指定。robots和sitemap也写的一塌糊涂。 130 | 5. 上面的问题,就是代理超时时间设定太长给用户造成的困扰。 131 | 132 | --- 133 | 134 | 当然极具喜感的是,那个贴的下面,有线通的技术人员出面道歉,并且告知——只要你把那台服务器设定为代理,就可以下载到真实的内容,还不受到速度限制——这种秘技。 135 | 136 | ——专业卖队友三十年。 137 | 138 | # 为什么大陆的网站都那么多广告 # 139 | 140 | ## 现象 ## 141 | 142 | 1. 自己的部落格出现了广告!我有收到钱吗? 143 | 2. google和百度首页也有广告? 144 | 3. 在新疆的同学们,为什么希望访问京东(大陆的一个电子商务网站),出现的却是这个叫做yiqifa.com的网站? 145 | 146 | --- 147 | 148 | ![google.png](google.png) 149 | 150 | --- 151 | 152 | ![newsbaidu.png](newsbaidu.png) 153 | 154 | --- 155 | 156 | ![2012121720233592.png](2012121720233592.png) 157 | 158 | ## 原理 ## 159 | 160 | 其实只要理解了运营商代理缓存,就能理解运营商广告,因为两者的技术原理是一样的。 161 | 162 | 这玩意电信叫做定向广告投放系统。就是拦截你的http请求,抢在服务器之前回复内容。术语叫做http会话劫持。你以为是站点给你的内容,其实是电信给你的。内容就是你的站点+广告。 163 | 164 | ## 解法 ## 165 | 166 | 这个不难解决。你先打给电信投诉。等几天后,如果还有,再打给工信部(这个机构你可能都没听说过,不过没关系,下面你会经常听到)投诉。电信会把你加入名单,你就没事了。 167 | 168 | 但是如果你是网站工作人员,事情就比较悲剧。工信部不允许网站主办者投诉针对自己的域名劫持事件。要求网站主去和ISP商量。 169 | 170 | 去。和中国电信商量的出什么东西来。 171 | 172 | # The Great Fire Well # 173 | 174 | ## 原理 ## 175 | 176 | GFW全称The Great Fire Well,是位于出入境边界上的一套IDS系统。他具备以下功能。 177 | 178 | * DNS劫持:对于某些域名,境内DNS服务器里强行存入错误的结果。 179 | * DNS污染:如果你查询国外的DNS服务器,抢先给你错误的结果。 180 | * IP丢弃:IP地址不好就把包丢掉。 181 | * 深度包检测和内容过滤:如果HTTP请求里面有些怪怪的东西就给双方各自一个RST。 182 | * 协议和流量分析模型:这个数据流模式看起来像是http?那可能就是http的某个包装。 183 | * 可疑内容加大丢包率:看起来很可疑,多丢个10%的包试试。 184 | 185 | 将来随时可能实施的还有: 186 | 187 | * 白名单功能:除了能够访问的,就是不能访问的。 188 | 189 | ## 测试1 ## 190 | 191 | 我们做一个很简单的实验来观测dns污染和劫持。 192 | 193 | $ dig www.facebook.com 194 | 195 | ;; QUESTION SECTION: 196 | ;www.facebook.com. IN A 197 | 198 | ;; ANSWER SECTION: 199 | www.facebook.com. 300 IN A 159.106.121.75 200 | 201 | --- 202 | 203 | $ whois 159.106.121.75 204 | 205 | NetRange: 159.106.0.0 - 159.106.255.255 206 | CIDR: 159.106.0.0/16 207 | OrgName: DoD Network Information Center 208 | OrgId: DNIC 209 | Address: 3990 E. Broad Street 210 | City: Columbus 211 | Country: US 212 | 213 | --- 214 | 215 | $ whois 31.13.70.17 216 | 217 | inetnum: 31.13.64.0 - 31.13.127.255 218 | netname: IE-FACEBOOK-20110418 219 | descr: Facebook Ireland Ltd 220 | country: IE 221 | org-name: Facebook Ireland Ltd 222 | org-type: LIR 223 | 224 | ## 测试2 ## 225 | 226 | 我们下面来看看DNS劫持。 227 | 228 | $ dig www.facebook.com @8.8.8.8 229 | 230 | ;; QUESTION SECTION: 231 | ;www.facebook.com. IN A 232 | 233 | ;; ANSWER SECTION: 234 | www.facebook.com. 300 IN A 59.24.3.173 235 | 236 | ## 测试3 ## 237 | 238 | 我们下面看看IP丢弃。 239 | 240 | $ ping 31.13.70.17 241 | 242 | PING 31.13.70.17 (31.13.70.17) 56(84) bytes of data. 243 | 64 bytes from 31.13.70.17: icmp_req=1 ttl=243 time=10.9 ms 244 | 64 bytes from 31.13.70.17: icmp_req=2 ttl=243 time=12.3 ms 245 | 246 | --- 31.13.70.17 ping statistics --- 247 | 2 packets transmitted, 2 received, 0% packet loss, time 1001ms 248 | rtt min/avg/max/mdev = 10.983/11.678/12.373/0.695 ms 249 | 250 | 下面是上海地区的。 251 | 252 | $ ping 31.13.70.17 253 | PING 31.13.70.17 (31.13.70.17) 56(84) bytes of data. 254 | 255 | --- 31.13.70.17 ping statistics --- 256 | 2 packets transmitted, 0 received, 100% packet loss, time 999ms 257 | 258 | --- 259 | 260 | $ mtr -n -r -c 10 31.13.70.17 261 | 262 | HOST: e135 Loss% Snt Last Avg Best Wrst StDev 263 | 1.|-- 192.168.1.1 0.0% 10 2.0 12.8 1.7 112.0 34.8 264 | 2.|-- 124.74.56.20 0.0% 10 3.9 12.4 3.0 81.3 24.3 265 | 3.|-- 124.74.57.229 0.0% 10 3.4 4.1 3.3 5.1 0.7 266 | 4.|-- 124.74.215.53 0.0% 10 3.7 4.6 3.6 6.7 1.1 267 | 5.|-- 202.101.63.242 0.0% 10 8.1 7.1 5.8 8.8 1.1 268 | 6.|-- 202.97.50.238 0.0% 10 5.0 5.4 4.2 8.5 1.2 269 | 7.|-- ??? 100.0 10 0.0 0.0 0.0 0.0 0.0 270 | 271 | 这里出现了一个奇怪的ip,202.97.50.238。whois显示这个ip不是中国电信的(至少不直接是)。在经过这个ip后,报文就丢失了。 272 | 273 | $ whois 202.97.50.238 274 | 275 | inetnum: 202.97.50.0 - 202.97.50.255 276 | netname: FSKWC 277 | descr: FSKWC NET 278 | 279 | ## 测试4 ## 280 | 281 | 我们下面看看深度包检测。 282 | 283 | $ wget http://python.org 284 | --2013-08-02 23:12:16-- http://python.org/ 285 | 正在解析主机 python.org (python.org)... 82.94.164.162, 2001:888:2000:d::a2 286 | 正在连接 python.org (python.org)|82.94.164.162|:80... 已连接。 287 | 已发出 HTTP 请求,正在等待回应... 200 OK 288 | 289 | 需要特别说明的是,目前python.org的下载已经不被封锁,下面这个结果是前年的。 290 | 291 | $ wget http://python.org/download/ 292 | 293 | 正在解析主机 python.org (python.org)... 82.94.164.162, 2001:888:2000:d::a2 294 | 正在连接 python.org (python.org)|82.94.164.162|:80... 已连接。 295 | 已发出 HTTP 请求,正在等待回应... 读取文件头错误 (Connection reset by peer)。 296 | 重试中。 297 | 298 | ## 吐槽 ## 299 | 300 | * python.org为啥被封 301 | 1. python.com误中副车说。 302 | 2. 版本号说。 303 | 3. 工具说。 304 | * python.org的反应 305 | 306 | --- 307 | 308 | ![python.png](python.png) 309 | 310 | ## 解法 ## 311 | 312 | 买翻墙VPN。 313 | 314 | 不要买太久,三个月到半年差不多了。两三年来,收费翻墙系统已经有三拨大的潮流了。 315 | 316 | 1. 开始是ssh。后来GFW升级了协议和流量分析模型,ssh里面传大量数据就会丢包,ssh挂掉。 317 | 2. 后来流行过openvpn。openvpn是基于ssl协议的,很快被抓住,挂掉。 318 | 3. 目前是pptp和l2tp,开始没人要玩的东西。因为这些协议会产生gre或者esp,ah之类的报文,在路由器上需要特殊的设定。但是由于这两种协议在商业上用的太多(windows中内置支持),因此要一次性断开会导致很多外企无法工作。目前还没有下手。 319 | 4. pptp很危险,因为他的安全性比l2tp差。有传闻说pptp所用的mppe40位加密已经遭到破解,mppe128安全性未知。 320 | 321 | ## 路由分离方案 ## 322 | 323 | 路由分离方案在有些地方也叫智能路由,其实和智能一毛钱关系都没有。 324 | 325 | 路由分离方案指的是,在NIC里可以查询到IPv4网段归属表。在这基础之上,将所有大陆的IP段配置成直接访问,非大陆IP段配置成vpn访问。这样可以有效减小无谓的vpn流量,而且可以避免vpn返流造成的缓慢,语言错误,再次穿越gfw造成的断开等问题。 326 | 327 | 现在大部分路由分离方案,都是源自chnroutes这个项目。这个项目的代码从nic上面下载最新的网段分配表,进行加工处理,产生可以用于各种环境的路由表配置文件。很多路由器或vpn中,配置了当vpn拨号后自动应用的路由表配置文件,并且保持配置文件一个月升级一次。 328 | 329 | ## 免费翻墙方案 ## 330 | 331 | 一天一换,请自行烦恼。 332 | 333 | * goagent 334 | * obfuscated-openssh 335 | * shadowsocks 336 | * openvpn混淆补丁 337 | * 其他方案 338 | * 我自己写的goproxy 339 | * 朋友写的openvpn跳频加密 340 | 341 | # 备案机制 # 342 | 343 | 备案是一项很扯的机制,细节就不介绍了。大概来说,就是去有关部门(别问我是哪里,有可能是工信部)注册自己为某个域名的所有者。这样,当网站出现问题的时候,可以反向查找到人。 344 | 345 | ## 备案白名单机制 ## 346 | 347 | 目前很多机房已经实施了备案白名单。当你通过备案,资料可查的时候。IDC机房的人才会把你的域名加入他们的防火墙白名单里去。如果有用户以白名单以外的域名,对机房的IP进行HTTP访问,就会收到两个RST。 348 | 349 | ## 问题 ## 350 | 351 | 至少到三年前为止(这两年在大陆没机器了,不大清楚),个人注册备案是需要身份证字号的。一个字号可以注册多个域名,可是——不能改! 352 | 353 | 到不是真的不能改。但是网站上给出的功能中没有修改,要修改必须到指定的地址提交书面材料。我的某位朋友曾经打算多注册几个域名屯起来,结果发现无法增加。后来觉得麻烦,就用他老婆的字号注册。我最后一次听说的时候,他刚三岁的儿子已经有个几个域名,我不知道孩子将来大了会不会埋怨父亲给自己找麻烦。。。 354 | 355 | ## 可能发生的问题 ## 356 | 357 | 呃,如果将来有人提出,要让大陆境外所有站点在大陆通过备案才允许访问,我一点都不会奇怪。这就是合法的白名单机制。 358 | -------------------------------------------------------------------------------- /chinanet/google.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shell909090/slides/d8aa123521daedee78e85e06f80c621929afaebd/chinanet/google.png -------------------------------------------------------------------------------- /chinanet/newsbaidu.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shell909090/slides/d8aa123521daedee78e85e06f80c621929afaebd/chinanet/newsbaidu.png -------------------------------------------------------------------------------- /chinanet/python.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shell909090/slides/d8aa123521daedee78e85e06f80c621929afaebd/chinanet/python.png -------------------------------------------------------------------------------- /context/PyConChina2014-stick-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shell909090/slides/d8aa123521daedee78e85e06f80c621929afaebd/context/PyConChina2014-stick-logo.png -------------------------------------------------------------------------------- /context/context.md: -------------------------------------------------------------------------------- 1 | # LICENSE 2 | 3 | [cc-by-sa3.0](http://creativecommons.org/licenses/by-sa/3.0/deed.zh) 4 | 5 | # 上下文切换技术 6 | 7 | ## 简述 8 | 9 | 在进一步之前,让我们先回顾一下各种上下文切换技术。 10 | 11 | 不过首先说明一点术语。当我们说“上下文”的时候,指的是程序在执行中的一个状态。通常我们会用调用栈来表示这个状态——栈记载了每个调用层级执行到哪里,还有执行时的环境情况等所有有关的信息。 12 | 13 | 当我们说“上下文切换”的时候,表达的是一种从一个上下文切换到另一个上下文执行的技术。而“调度”指的是决定哪个上下文可以获得接下去的CPU时间的方法。 14 | 15 | ## 进程 16 | 17 | 进程是一种古老而典型的上下文系统,每个进程有独立的地址空间,资源句柄,他们互相之间不发生干扰。 18 | 19 | 每个进程在内核中会有一个数据结构进行描述,我们称其为进程描述符。这些描述符包含了系统管理进程所需的信息,并且放在一个叫做任务队列的队列里面。 20 | 21 | 很显然,当新建进程时,我们需要分配新的进程描述符,并且分配新的地址空间(和父地址空间的映射保持一致,但是两者同时进入COW状态)。这些过程需要一定的开销。 22 | 23 | ## 进程状态 24 | 25 | 忽略去linux内核复杂的状态转移表,我们实际上可以把进程状态归结为三个最主要的状态:就绪态,运行态,睡眠态。这就是任何一本系统书上都有的三态转换图。 26 | 27 | 就绪和执行可以互相转换,基本这就是调度的过程。而当执行态程序需要等待某些条件(最典型就是IO)时,就会陷入睡眠态。而条件达成后,一般会自动进入就绪。 28 | 29 | ## 阻塞 30 | 31 | 当进程需要在某个文件句柄上做IO,这个fd又没有数据给他的时候,就会发生阻塞。具体来说,就是记录XX进程阻塞在了XX fd上,然后将进程标记为睡眠态,并调度出去。当fd上有数据时(例如对端发送的数据到达),就会唤醒阻塞在fd上的进程。进程会随后进入就绪队列,等待合适的时间被调度。 32 | 33 | 阻塞后的唤醒也是一个很有意思的话题。当多个上下文阻塞在一个fd上(虽然不多见,但是后面可以看到一个例子),而且fd就绪时,应该唤醒多少个上下文呢?传统上应当唤醒所有上下文,因为如果仅唤醒一个,而这个上下文又不能消费所有数据时,就会使得其他上下文处于无谓的死锁中。 34 | 35 | 但是有个著名的例子——accept,也是使用读就绪来表示收到的。如果试图用多个线程来accept会发生什么?当有新连接时,所有上下文都会就绪,但是只有第一个可以实际获得fd,其他的被调度后又立刻阻塞。这就是惊群问题[thundering herd problem](http://en.wikipedia.org/wiki/Thundering_herd_problem)。 36 | 37 | 现代linux内核已经解决了这个问题,方法惊人的简单——accept方法加锁。(inet\_connection\_sock.c:inet\_csk\_wait\_for\_connect) 38 | 39 | PS: 根据我最新的代码研读结果,并不是加锁来解决这个问题的。这个问题是否真正的在内核被解决了,我目前还存疑。等我搞明白了再补充。 40 | 41 | ## 线程 42 | 43 | 线程是一种轻量进程,实际上在linux内核中,两者几乎没有差别,除了一点——线程并不产生新的地址空间和资源描述符表,而是复用父进程的。 44 | 45 | 但是无论如何,线程的调度和进程一样,必须陷入内核态。 46 | 47 | # 传统网络服务模型 48 | 49 | ## 进程模型 50 | 51 | 为每个客户分配一个进程。优点是业务隔离,在一个进程中出现的错误不至于影响整个系统,甚至其他进程。Oracle传统上就是进程模型。 52 | 53 | 缺点是进程的分配和释放有非常高的成本。因此Oracle需要连接池来保持连接减少新建和释放,同时尽量复用连接而不是随意的新建连接。 54 | 55 | ## 线程模型 56 | 57 | 为每客户分配一个线程。优点是更轻量,建立和释放速度更快,而且多个上下文间的通讯速度非常快。 58 | 59 | 缺点是一个线程出现问题容易将整个系统搞崩溃。 60 | 61 | ## 一个例子 62 | 63 | [py_http_fork_thread.py](py_http_fork_thread.py) 64 | 65 | 在这个例子中,线程模式和进程模式可以轻易的互换。 66 | 67 | ## 如何工作的 68 | 69 | 1. 父进程监听服务端口 70 | 2. 在有新连接建立的时候,父进程执行fork,产生一个子进程副本 71 | 3. 如果子进程需要的话,可以exec(例如CGI) 72 | 4. 父进程执行(理论上应当先执行子进程,因为exec执行的快可以避免COW)到accept后,发生阻塞 73 | 5. 上下文调度,内核调度器选择下一个上下文,如无意外,应当就是刚刚派生的子进程 74 | 6. 子进程进程进入读取处理状态,阻塞在read调用上,所有上下文均进入睡眠态 75 | 7. 随着SYN或者数据报文到来,CPU会唤醒对应fd上阻塞的上下文(wait_queue),切换到就绪态,并加入调度队列 76 | 8. 上下文继续执行到下一个阻塞调用,或者因为时间片耗尽被挂起 77 | 78 | 关于更多细节,可以看[这里](http://www.slideshare.net/llj098/epoll-from-the-kernel-side)。这篇文章里还介绍了epoll的工作细节。 79 | 80 | ## 评价 81 | 82 | * 同步模型,编写自然,每个上下文可以当作其他上下文不存在一样的操作,每次读取数据可以当作必然能读取到。 83 | * 进程模型自然的隔离了连接。即使程序复杂且易崩溃,也只影响一个连接而不是在整个系统。 84 | * 生成和释放开销很大(效率测试的进程fork和线程模式开销测试),需要考虑复用。 85 | * 进程模式的多客户通讯比较麻烦,尤其在共享大量数据的时候。 86 | 87 | # C10K问题 88 | 89 | ## 描述 90 | 91 | 当同时连接数在10K左右时,传统模型就不再适用。实际上在效率测试报告的线程切换开销一节可以看到,超过1K后性能就差的一塌糊涂了。 92 | 93 | 更细节描述,可以看[这里](http://www.kegel.com/c10k.html)。 94 | 95 | ## 进程模型的问题 96 | 97 | 在C10K的时候,启动和关闭这么多进程是不可接受的开销。事实上单纯的进程fork模型在C1K时就应当抛弃了。 98 | 99 | Apache的prefork模型,是使用预先分配(pre)的进程池。这些进程是被复用的。但即便是复用,本文所描述的很多问题仍不可避免。 100 | 101 | ## 线程模式的问题 102 | 103 | 从任何测试都可以表明,线程模式比进程模式更耐久一些,性能更好。但是在面对C10K还是力不从心的。问题是,线程模式的问题出在哪里呢? 104 | 105 | ## 内存? 106 | 107 | 有些人可能认为线程模型的失败首先在于内存。如果你这么认为,一定是因为你查阅了非常老的资料,并且没仔细思考过。 108 | 109 | 你可能看到资料说,一个线程栈会消耗8M内存(linux默认值,ulimit可以看到),512个线程栈就会消耗4G内存,而10K个线程就是80G。所以首先要考虑调整栈深度,并考虑爆栈问题。 110 | 111 | 听起来很有道理,问题是——linux的栈是通过缺页来分配内存的([How does stack allocation work in Linux?](http://unix.stackexchange.com/questions/145557/how-does-stack-allocation-work-in-linux)),不是所有栈地址空间都分配了内存。因此,8M是*最大*消耗,实际的内存消耗只会略大于实际需要的内存(内部损耗,每个在4k以内)。但是内存一旦被分配,就很难回收(除非线程结束),这是线程模式的缺陷。 112 | 113 | 这个问题提出的前提是,32位下地址空间有限。虽然10K个线程不一定会耗尽内存,但是512个线程一定会耗尽地址空间。然而这个问题对于目前已经成为主流的64位系统来说根本不存在。 114 | 115 | ## 内核陷入开销? 116 | 117 | 所谓内核陷入开销,就是指CPU从非特权转向特权,并且做输入检查的一些开销。这些开销在不同的系统上差异很大。 118 | 119 | 线程模型主要通过陷入切换上下文,因此陷入开销大听起来有点道理。 120 | 121 | 实际上,这也是不成立的。线程在什么时候发生陷入切换?正常情况下,应当是IO阻塞的时候。同样的IO量,难道其他模型就不需要陷入了么?只是非阻塞模型有很大可能直接返回,并不发生上下文切换而已。从原理上说,由于非阻塞模式调用可能失败,因此内核陷入开销的影响反而更大。 122 | 123 | 效率测试报告的基础调用开销一节,测量了当代操作系统上内核陷入开销。在样机上,典型的内核陷入开销在50ns这个量级。这个开销不算太小,但是却不足以说明缓慢。如果把内核陷入跑满一颗CPU,这颗CPU可以执行20M次陷入。每次陷入吞吐500字节数据的话,可以满足10G吞吐。何况在大流量的情况下,必然是大颗粒IO。大粒度IO陷入次数更少,也更节约CPU。 124 | 125 | ## 线程模型的问题在于切换成本高 126 | 127 | 熟悉linux内核的应该知道,近代linux调度器经过几个阶段的发展。 128 | 129 | 1. linux2.4的调度器。 130 | 2. O(1)调度器。 131 | 3. CFS。 132 | 133 | 实际上直到O(1),调度器的调度复杂度才和队列长度无关。在此之前,过多的线程会使得开销随着线程数增长(不保证线性)。 134 | 135 | O(1)调度器看起来似乎是完全不随着线程的影响。但是这个调度器有显著的缺点——难于理解和维护,并且在一些情况下会导致交互式程序响应缓慢。 136 | 137 | CFS使用红黑树管理就绪队列。每次调度,上下文状态转换,都会查询或者变更红黑树。红黑树的开销大约是O(logm),其中m大约为活跃上下文数(准确的说是同优先级上下文数),大约和活跃的客户数相当。 138 | 139 | 因此,每当线程试图读写网络,并遇到阻塞时,都会发生O(logm)级别的开销。而且每次收到报文,唤醒阻塞在fd上的上下文时,同样要付出O(logm)级别的开销。 140 | 141 | 这里参考了[这篇文章](http://www.ibm.com/developerworks/cn/linux/l-cn-scheduler/),在此表示感谢。这篇文章里面清楚的介绍了各种linux调度器技术(不包括bfs)。 142 | 143 | ## 分析 144 | 145 | O(logm)的开销看似并不大,但是却是一个无法接受的开销。因为IO阻塞是一个经常发生的事情。每次IO阻塞,都会发生开销。而且决定活跃线程数的是用户,这不是我们可控制的。更糟糕的是,当性能下降,响应速度下降时。同样的用户数下,活跃上下文会上升(因为响应变慢了)。这会进一步拉低性能。 146 | 147 | 问题的关键在于,http服务并不需要对每个用户完全公平,偶尔某个用户的响应时间大大的延长了是可以接受的。在这种情况下,使用红黑树去组织待处理fd列表(其实是上下文列表),并且反复计算和调度,是无谓的开销。 148 | 149 | # 多路复用 150 | 151 | ## 简述 152 | 153 | 要突破C10K问题,必须减少系统内活跃上下文数(其实未必,例如换一个调度器,例如使用RT的SCHED_RR),因此就要求一个上下文同时处理多个链接。而要做到这点,就必须在每次系统调用读取或写入数据时立刻返回。否则上下文持续阻塞在调用上,如何能够复用?这要求fd处于非阻塞状态,或者数据就绪。 154 | 155 | 上文所说的所有IO操作,其实都特指了他的阻塞版本。所谓阻塞,就是上下文在IO调用上等待直到有合适的数据为止。这种模式给人一种“只要读取数据就必定能读到”的感觉。而非阻塞调用,就是上下文立刻返回。如果有数据,带回数据。如果没有数据,带回错误(EAGAIN)。因此,“虽然发生错误,但是不代表出错”。 156 | 157 | 但是即使有了非阻塞模式,依然绕不过就绪通知问题。如果没有合适的就绪通知技术,我们只能在多个fd中盲目的重试,直到碰巧读到一个就绪的fd为止。这个效率之差可想而知。 158 | 159 | 在就绪通知技术上,有两种大的模式——就绪事件通知和异步IO。其差别简要来说有两点。就绪通知维护一个状态,由用户读取。而异步IO由系统调用用户的回调函数。就绪通知在数据就绪时就生效,而异步IO直到数据IO完成才发生回调。 160 | 161 | linux下的主流方案一直是就绪通知,其内核态异步IO方案甚至没有被封装到glibc里去。围绕就绪通知,linux总共提出过三种解决方案。我们绕过select和poll方案,看看epoll方案的特性。 162 | 163 | 另外提一点。有趣的是,当使用了epoll后(更准确说只有在LT模式下),fd是否为非阻塞其实已经不重要了。因为epoll保证每次去读取的时候都能读到数据,因此不会阻塞在调用上。 164 | 165 | ## epoll 166 | 167 | 用户可以新建一个epoll文件句柄,并且将其他fd和这个"epoll fd"关联。此后可以通过epoll fd读取到所有就绪的文件句柄。 168 | 169 | epoll有两大模式,ET和LT。LT模式下,每次读取就绪句柄都会读取出完整的就绪句柄。而ET模式下,只给出上次到这次调用间新就绪的句柄。换个说法,如果ET模式下某次读取出了一个句柄,这个句柄从未被读取完过——也就是从没有从就绪变为未就绪。那么这个句柄就永远不会被新的调用返回,哪怕上面其实充满了数据——因为句柄无法经历从非就绪变为就绪的过程。 170 | 171 | 类似CFS,epoll也使用了红黑树——不过是用于组织加入epoll的所有fd。epoll的就绪列表使用的是双向队列。这方便系统将某个fd加入队列中,或者从队列中解除。 172 | 173 | 要进一步了解epoll的具体实现,可以参考这篇[linux下poll和epoll内核源码剖析](http://www.slideshare.net/donghao/linuxpollepoll-2173984)。 174 | 175 | ## 性能 176 | 177 | 如果使用非阻塞函数,就不存在阻塞IO导致上下文切换了,而是变为时间片耗尽被抢占(大部分情况下如此),因此读写的额外开销被消除。而epoll的常规操作,都是O(1)量级的。而epoll wait的复制动作,则和当前需要返回的fd数有关(在LT模式下几乎就等同于上面的m,而ET模式下则会大大减少)。 178 | 179 | 但是epoll存在一点细节问题。epoll fd的管理使用红黑树,因此在加入和删除时需要O(logn)复杂度(n为总连接数),而且关联操作还必须每个fd调用一次。因此在大连接量下频繁建立和关闭连接仍然有一定性能问题(超短连接)。不过关联操作调用毕竟比较少。如果确实是超短连接,tcp连接和释放开销就很难接受了,所以对总体性能影响不大。 180 | 181 | ## 固有缺陷 182 | 183 | 原理上说,epoll实现了一个wait\_queue的回调函数,因此原理上可以监听任何能够激活wait\_queue的对象。但是epoll的最大问题是无法用于普通文件,因为普通文件始终是就绪的——虽然在读取的时候不是这样。 184 | 185 | 这导致基于epoll的各种方案,一旦读到普通文件上下文仍然会阻塞。golang为了解决这个问题,在每次调用syscall的时候,会独立的启动一个线程,在独立的线程中进行调用。因此golang在IO普通文件的时候网络不会阻塞。 186 | 187 | 推测libaio解决了这个问题(TODO: test)。 188 | 189 | # 事件通知机制下的几种程序设计模型 190 | 191 | ## 简述 192 | 193 | 使用通知机制的一大缺憾就是,用户进行IO操作后会陷入茫然——IO没有完成,所以当前上下文不能继续执行。但是由于复用线程的要求,当前线程还需要接着执行。所以,在如何进行异步编程上,又分化出数种方案。 194 | 195 | ## 用户态调度 196 | 197 | 首先需要知道的一点就是,异步编程大多数情况下都伴随着用户态调度问题——即使不使用上下文技术。 198 | 199 | 因为系统不会自动根据fd的阻塞状况来唤醒合适的上下文了,所以这个工作必须由其他人——一般就是某种框架——来完成。 200 | 201 | 你可以想像一个fd映射到对象的大map表,当我们从epoll中得知某个fd就绪后,需要唤醒某种对象,让他处理fd对应的数据。 202 | 203 | 当然,实际情况会更加复杂一些。原则上所有不占用CPU时间的等待都需要中断执行,陷入睡眠,并且交由某种机构管理,等待合适的机会被唤醒。例如sleep,或是文件IO,还有lock。更精确的说,所有在内核里面涉及到wait_queue的,在框架里面都需要做这种机制——也就是把内核的调度和等待搬到用户态来。 204 | 205 | 当然,其实也有反过来的方案——就是把程序扔到内核里面去。其中最著名的实例大概是微软的http服务器了。 206 | 207 | 这个所谓的“可唤醒可中断对象”,用的最多的就是协程。 208 | 209 | ## 协程 210 | 211 | 协程是一种编程组件,可以在不陷入内核的情况进行上下文切换。如此一来,我们就可以把协程上下文对象关联到fd,让fd就绪后协程恢复执行。 212 | 213 | 当然,由于当前地址空间和资源描述符的切换无论如何需要内核完成,因此协程所能调度的,只有在同一进程中的不同上下文而已。 214 | 215 | ## 如何做到 216 | 217 | 这是如何做到的呢? 218 | 219 | 我们在内核里实行上下文切换的时候,其实是将当前所有寄存器保存到内存中,然后从另一块内存中载入另一组已经被保存的寄存器。对于图灵机来说,当前状态寄存器意味着机器状态——也就是整个上下文。其余内容,包括栈上内存,堆上对象,都是直接或者间接的通过寄存器来访问的。 220 | 221 | 但是请仔细想想,寄存器更换这种事情,似乎不需要进入内核态么。事实上我们在用户态切换的时候,就是用了类似方案。 222 | 223 | C coroutine的实现,基本大多是保存现场和恢复之类的过程。python则是保存当前thread的top frame(greenlet)。 224 | 225 | 但是非常悲剧的,纯用户态方案(setjmp/longjmp)在多数系统上执行的效率很高,但是并不是为了协程而设计的。setjmp并没有拷贝整个栈(大多数的coroutine方案也不应该这么做),而是只保存了寄存器状态。这导致新的寄存器状态和老寄存器状态共享了同一个栈,从而在执行时互相破坏。而完整的coroutine方案应当在特定时刻新建一个栈。 226 | 227 | 而比较好的方案(makecontext/swapcontext)则需要进入内核(sigprocmask),这导致整个调用的性能非常低。 228 | 229 | 关于setjmp/longjmp,你可以参考[CS360 Lecture notes -- Setjmp](http://web.eecs.utk.edu/~huangj/cs360/360/notes/Setjmp/lecture.html),和[setjmp 的正确使用 | 云风的 BLOG](http://blog.codingnow.com/2010/05/setjmp.html)。 230 | 231 | ## 协程与线程的关系 232 | 233 | 首先我们可以明确,协程不能调度其他进程中的上下文。而后,每个协程要获得CPU,都必须在线程中执行。因此,协程所能利用的CPU数量,和用于处理协程的线程数量直接相关。 234 | 235 | 作为推论,在单个线程中执行的协程,可以视为单线程应用。这些协程,在未执行到特定位置(基本就是阻塞操作)前,是不会被抢占,也不会和其他CPU上的上下文发生同步问题的。因此,一段协程代码,中间没有可能导致阻塞的调用,执行在单个线程中。那么这段内容可以被视为同步的。 236 | 237 | 我们经常可以看到某些协程应用,一启动就是数个进程。这并不是跨进程调度协程。一般来说,这是将一大群fd分给多个进程,每个进程自己再做fd-协程对应调度。 238 | 239 | ## 基于就绪通知的协程框架 240 | 241 | 1. 首先需要包装read/write,在发生read的时候检查返回。如果是EAGAIN,那么将当前协程标记为阻塞在对应fd上,然后执行调度函数。 242 | 2. 调度函数需要执行epoll(或者从上次的返回结果缓存中取数据,减少内核陷入次数),从中读取一个就绪的fd。如果没有,上下文应当被阻塞到至少有一个fd就绪。 243 | 3. 查找这个fd对应的协程上下文对象,并调度过去。 244 | 4. 当某个协程被调度到时,他多半应当在调度器返回的路上——也就是read/write读不到数据的时候。因此应当再重试读取,失败的话返回1。 245 | 5. 如果读取到数据了,直接返回。 246 | 247 | 这样,异步的数据读写动作,在我们的想像中就可以变为同步的。而我们知道同步模型会极大降低我们的编程负担。 248 | 249 | ## CPS模型 250 | 251 | 其实这个模型有个更流行的名字——回调模型。之所以扯上CPS这么高大上的玩意,主要是里面涉及不少有趣的话题。 252 | 253 | 首先是回调模型的大致过程。在IO调用的时候,同时传入一个函数,作为返回函数。当IO结束时,调用传入的函数来处理下面的流程。这个模型听起来挺简单的。 254 | 255 | 然后是[CPS](http://en.wikipedia.org/wiki/Continuation-passing_style)。用一句话来描述这个模型——他把一切操作都当作了IO,无论干什么,结果要通过回调函数来返回。从这个角度来说,IO回调模型只能被视作CPS的一个特例。 256 | 257 | 例如,我们需要计算1+2*3,在cps里面就需要这么写: 258 | 259 | mul(lambda x: add(pprint.pprint, x, 1), 2, 3) 260 | 261 | 其中mul和add在python里面如下定义: 262 | 263 | add = lambda f, *nums: f(sum(nums)) 264 | mul = lambda f, *nums: f(reduce(lambda x,y: x*y, nums)) 265 | 266 | 而且由于python没有TCO,所以这样的写法会产生非常多的frame。 267 | 268 | 但是要正确理解这个模型,你需要仔细思考一下以下几个问题: 269 | 270 | * 函数的调用过程为什么必须是一个栈? 271 | * IO过程在什么时间发生?调用发生时,还是回调时? 272 | * 回调函数从哪里调用?如果当时利用工具去看上下文的话,调用栈是什么样子的? 273 | 274 | ## 函数组件和返回值 275 | 276 | 不知道你是否思考过为什么函数调用层级(上下文栈)会被表述为一个栈——是否有什么必要性,必须将函数调用的过程定义为一个栈呢? 277 | 278 | 原因就是返回值和同步顺序。对于大部分函数,我们需要得到函数计算的返回值。而要得到返回值,调用者就必须阻塞直到被调用者返回为止。因此调用者的执行状态就必须被保存,等到被调用者返回后继续——从这点来说,调用其实是最朴素的上下文切换手段。而对于少部分无需返回的函数,我们又往往需要他的顺序外部效应——例如干掉了某个进程,开了一个灯,或者仅仅是在环境变量里面添加了一项内容。而顺序外部效应同样需要等待被调用者返回以表明这个外部效应已经发生。 279 | 280 | 那么,如果我们不需要返回值也不需要顺序的外部效应呢?例如启动一个背景程序将数据发送到对端,无需保证发送成功的情况下。或者是开始一个数据抓取行为,无需保证抓取的成功。 281 | 282 | 通常这种需求我们就凑合着用一个同步调用混过去了——反正问题也不严重。但是对于阻塞相当严重的情况而言,很多人还是会考虑到将这个行为做成异步过程。目前最流行的异步调用分解工具就是mq——不仅异步,而且分布。当然,还有一个更简单的非分布方案——开一个coroutine。 283 | 284 | 而CPS则是另一个方向——函数的返回值可以不返回调用者,而是返回给第三者。 285 | 286 | ## IO过程在什么时间发生 287 | 288 | 其实这个问题的核心在于——整个回调模型是基于多路复用的还是基于异步IO的? 289 | 290 | 原则上两者都可以。你可以监听fd就绪,也可以监听IO完成。当然,即使监听IO完成,也不代表使用了内核态异步接口。很可能只是用epoll封装的而已。 291 | 292 | ## 回调函数的上下文环境 293 | 294 | 这个问题则需要和上面提到的“用户态调度框架”结合起来说。IO回调注册的实质是将回调函数绑定到某个fd上——就如同将coroutine绑定上去那样。只是coroutine允许你顺序的执行,而callback则会切碎函数。当然,大部分实现中,使用callback也有好处——coroutine的最小切换开销也在50ns,而call本身则只有2ns。 295 | 296 | ## 状态机模型 297 | 298 | 状态机模型是一个更难于理解和编程的模型,其本质是每次重入。 299 | 300 | 想像你是一个周期失忆的病人(就像“一周的朋友”那样)。那么你如何才能完成一项需要跨越周期的工作呢?例如刺绣,种植作物,或者——交一个男朋友。 301 | 302 | 当然,类比到失忆病人的例子上必须有一点限制。正常的生活技能,还有一些常识性的东西必须不能在周期失忆范围内。例如重新学习认字什么的可没人受的了。 303 | 304 | 答案就是——做笔记。每次重复失忆后,你需要阅读自己的笔记,观察上次做到哪个步骤,下一个步骤是什么。这需要将一个工作分解为很多步骤,在每个步骤内“重入”直到步骤完成,转移到下一个状态。 305 | 306 | 同理,在状态机模型解法里,每次执行都需要推演合适的状态,直到工作完成。这个模型已经很少用到了,因为相比回调函数来说,状态机模型更难理解和使用,性能差异也不大。 307 | 308 | 最后顺带一提,交一个男友的方案和其他几个略有不同,主要靠颜好高冷反差萌,一般人就不要尝试挑战了。。。当然一般人也不会一周失忆一次,毕竟生活不是韩剧也不是日本动漫。。。 309 | -------------------------------------------------------------------------------- /context/context_slide.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | context_slide 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 |
17 |
18 |
19 |

上下文切换技术简介

20 | 21 |
22 |
23 |

logo

24 |

PyConChina2014-stick-logo.png

25 | 26 |
27 |
28 |

自我介绍

29 |

shell909090,七牛程序员,主要用python和golang。

30 | 31 |
32 |
33 |

LICENSE

34 |

cc-by-sa3.0

35 | 36 |
37 |
38 |
39 |
40 |
41 |
42 |

系统级上下文技术

43 | 44 |
45 |
46 |

术语

47 |
    48 |
  • 上下文 49 |
      50 |
    • 调用栈
    • 51 |
    • 指向栈的寄存器
    • 52 |
  • 53 |
  • 切换 54 |
      55 |
    • 当前执行的一个上下文变为另一个上下文
    • 56 |
    • 不是上下文的创建和消灭,而是上下文获得CPU时间
    • 57 |
  • 58 |
  • 调度 59 |
      60 |
    • 决定哪个上下文应当获得CPU了
    • 61 |
  • 62 |
  • 进程 63 |
      64 |
    • 分配资源的单位
    • 65 |
  • 66 |
  • 线程 67 |
      68 |
    • 分配CPU的单位
    • 69 |
  • 70 |
71 | 72 |
73 |
74 |

进程状态

75 |
    76 |
  • 运行: 得到CPU
  • 77 |
  • 就绪: 可以得到CPU,但是尚未调度到
  • 78 |
  • 睡眠: 等待某些条件,因此获得了CPU也没用
  • 79 |
80 | 81 |
82 |
83 |

性能

84 |
    85 |
  • fork的开销在40-50us不等
  • 86 |
  • pthread(nptl)的开销在9.5us左右
  • 87 |
88 | 89 |
90 |
91 |
92 |
93 |
94 |
95 |

传统网络服务模型

96 | 97 |
98 |
99 |

如何工作

100 |
    101 |
  1. 父进程监听服务端口
  2. 102 |
  3. 在有新连接建立的时候,父进程执行fork,产生一个子进程副本
  4. 103 |
  5. 如果子进程需要的话,可以exec(例如CGI)
  6. 104 |
  7. 父进程执行到accept后,发生阻塞
  8. 105 |
  9. 上下文调度,内核调度器选择下一个上下文,一般就是刚派生的子进程
  10. 106 |
  11. 子进程进程进入读取处理状态,阻塞在read调用上,所有上下文均进入睡眠态
  12. 107 |
  13. 随着SYN或者数据报文到来,CPU会唤醒对应fd上阻塞的上下文
  14. 108 |
  15. 上下文切换到就绪态,并加入调度队列
  16. 109 |
  17. 继续执行到下一个阻塞调用,或者因为时间片耗尽被挂起
  18. 110 |
111 | 112 |
113 |
114 |

fork的细节

115 |

产生当前进程的同等副本,在fork时并不立刻分配新的资源。

116 |

而是在其中某个进程执行了写入时发生复制,这叫做COW。

117 |

理论上fork应当先执行子进程,因为有很大可能执行exec。

118 |

而exec执行的快可以避免COW。

119 | 120 |
121 |
122 |

阻塞-调度的细节

123 |
    124 |
  • 当试图读取数据而无数据可读时,既不能得到数据也不能返回。
  • 125 |
  • 此时上下文处于等待某些条件的状态。
  • 126 |
  • 在此种条件下,上下文不参与调度。系统会自动选择一个就绪的上下文去切换。
  • 127 |
  • 当前上下文被挂入等待条件的wait_queue队列。

  • 128 |
  • 当条件就绪,例如收到网络数据的时候,会唤醒wait_queue上挂起的所有上下文。

  • 129 |
  • 不能只唤醒一个。
  • 130 |
  • 因为如果一个上下文不能消费所有数据,会使得剩余上下文无谓阻塞。
  • 131 |
  • 但是如果数据只够一个上下文消费,会发生惊群。
  • 132 |
133 | 134 |
135 |
136 |

评价

137 |
    138 |
  • 同步模型编写自然,每次读取数据可以当作必然能读取到。
  • 139 |
  • 进程模型隔离性好,每个上下文可以当作其他上下文不存在一样的操作。
  • 140 |
  • 即使程序复杂且易崩溃,也只影响一个连接而不是在整个系统。
  • 141 |
  • 生成和释放开销很大,需要考虑复用。
  • 142 |
  • 进程模式的多客户通讯比较麻烦,尤其在共享大量数据的时候。
  • 143 |
144 | 145 |
146 |
147 |
148 |
149 |
150 |
151 |

C10K问题

152 | 153 |
154 |
155 |

上下文重复建立开销

156 |

通过上下文池和复用上下文解决。

157 |

上下文不销毁,而是从头开始循环,获得下一个请求并处理。

158 | 159 |
160 |
161 |

线程复用模式的问题

162 |

线程复用模式仍然不够快,往往会被认为以下两个因素。

163 |
    164 |
  • 内存
  • 165 |
  • 内核陷入
  • 166 |
167 | 168 |
169 |
170 |

linux栈分配原理

171 |

linux下只为栈保留地址空间,而不映射内存。

172 |

当地址空间首次被访问时,才分配物理内存。因此大量线程不会消耗无谓内存。

173 |

8M是最大内存限制。

174 |

当栈回退时,物理页面映射不释放。

175 | 176 |
177 |
178 |

内核陷入开销

179 |

很小,最低只有50ns。而普通函数调用也有2ns。

180 |

而且现代高效模型也要通过read陷入来获得数据,这个开销未能避免。

181 | 182 |
183 |
184 |

内核调度器

185 |
    186 |
  1. linux2.4的调度器。
  2. 187 |
  3. O(1)调度器。
  4. 188 |
  5. CFS。
  6. 189 |
190 | 191 |
192 |
193 |

开销

194 |

yield每次耗费的时间随活跃线程数变化曲线

195 |

t_yield.png

196 | 197 |
198 |
199 | 200 |

lock每次耗费的时间随活跃线程书变化曲线

201 |

t_lock.png

202 | 203 |
204 |
205 |

lock消耗为什么不随着活跃线程数上升

206 |

因为lock调用futex,而将线程至于睡眠态。

207 | 208 |
209 |
210 |
211 |
212 |
213 |
214 |

多路复用

215 | 216 |
217 |
218 |

简述

219 |
    220 |
  • 要突破C10K,就要减少活跃上下文数。因此一个上下文必须能处理多个连接。
  • 221 |
  • 因此系统调用时必须立刻返回。否则会塞住上下文,阻碍复用。
  • 222 |
  • 非阻塞调用可以做到立刻返回,但是如何选择要读的文件句柄?
  • 223 |
224 | 225 |
226 |
227 |

就绪通知 vs 异步IO

228 |
    229 |
  • 由用户读取 vs 由系统回调
  • 230 |
  • 在数据就绪时通知 vs 在数据IO完成后回调
  • 231 |
232 | 233 |
234 |
235 |

linux的就绪通知发展

236 |
    237 |
  • select: 每次调用需要传入fd数组,有最大限制
  • 238 |
  • poll: 每次调用需要传入fd数组,无最大限制
  • 239 |
  • epoll: 每次调用无需传入数组,没有最大限制
  • 240 |
241 | 242 |
243 |
244 |

epoll

245 |
    246 |
  • ET: 状态转变时触发
  • 247 |
  • LT: 读取所有就绪句柄
  • 248 |
249 | 250 |
251 |
252 |

性能分析

253 |
    254 |
  • epoll_wait/ep_poll_callback: O(1)级
  • 255 |
  • epoll_ctl: O(logn)级
  • 256 |
257 | 258 |
259 |
260 |

固有缺陷

261 |

无法用于普通文件。

262 | 263 |
264 |
265 |
266 |
267 |
268 |
269 |

事件通知机制下的程序设计模型

270 | 271 |
272 |
273 |

用户态调度

274 |

知道了哪些fd就绪,如何处理这些fd?

275 |

将对应fd映射到处理这个fd的某组过程上的结构设计,被称为用户态调度。

276 |

这样,当我们知道某个fd就绪时,可以激活对应的过程。

277 |

更广义的说,各种内核态带有wait_queue的对象,在用户态调度上都要做类似处理。

278 |

例如时钟和锁。

279 | 280 |
281 |
282 | 283 |

因此用户态调度可类比于内核态调度,但是用户态调度没有抢占。

284 |

当这个过程长期执行不返回时,其他句柄不能被及时处理。

285 |

用公平性换取效率。

286 | 287 |
288 |
289 |

coroutine

290 |

切换机器状态字和执行栈不需要进入内核,这也是用户态上下文切换的核心思想。

291 |

setjmp/longjmp只能切换状态字,而没有独立的执行栈。

292 |

因此setjmp/longjmp用于coroutine总是有点问题,建议用ucontext。

293 |

greenlet也是利用新建独立执行栈和替换当前栈顶部frame的方法来切换。

294 | 295 |
296 |
297 |

协程和线程的关系和区别

298 |
    299 |
  • 协程要获得CPU,必须在线程中执行
  • 300 |
  • 协程无法跨进程调度
  • 301 |
  • 同时执行的协程数不能大于容纳他的线程数
  • 302 |
  • 协程没有抢占
  • 303 |
304 |

因此,单线程中执行的协程,可以视为单线程应用。

305 |

在阻塞调用之间,访问数据对象时是不需要加锁的。

306 | 307 |
308 |
309 |

golang协程性能

310 |

sched调用延迟随goroutine数变化

311 |

g_sched.png

312 | 313 |
314 |
315 | 316 |

chan调用延迟随goroutine数变化

317 |

g_chan.png

318 | 319 |
320 |
321 | 322 |

lock调用延迟随goroutine数变化

323 |

g_lock.png

324 | 325 |
326 |
327 |

python协程性能

328 |
    329 |
  • yield的开销大约是22ns
  • 330 |
  • greenlet的开销大约是500ns
  • 331 |
332 | 333 |
334 |
335 | 336 |

yield from随层数变化

337 |

py_yieldfrom.png

338 | 339 |
340 |
341 |

基于就绪通知的协程框架

342 |
    343 |
  1. 包装read/write,检查返回。
  2. 344 |
  3. 如果是EAGAIN,将当前协程绑定到fd上,然后执行调度函数。
  4. 345 |
  5. 调度函数epoll,读一个就绪的fd。如果没有,阻塞直到至少一个fd就绪。
  6. 346 |
  7. 查找这个fd对应的协程上下文对象,并切换过去。
  8. 347 |
  9. 当某个协程被切换到时,应当再重试读取,失败的话重复2。
  10. 348 |
  11. 如果读取到数据了,返回。
  12. 349 |
350 | 351 |
352 |
353 | 354 |

这样,异步的数据读写动作,在我们的想像中就可以变为同步的。

355 |

而同步模型会极大降低我们的编程负担。

356 | 357 |
358 |
359 |

回调模型

360 |

所谓回调模型就是,在IO调用的时候,同时传入一个函数,作为返回函数。

361 |

当IO结束时,调用传入的函数来处理下面的流程。

362 | 363 |
364 |
365 |

CPS

366 |

用一句话来描述CPS——他把一切操作都当作了IO。

367 |

无论干什么,结果要通过回调函数来返回。

368 |

从这个角度来说,IO回调模型只能被视作CPS的一个特例。

369 |
add = lambda f, *nums: f(sum(nums))
370 | mul = lambda f, *nums: f(reduce(lambda x,y: x*y, nums))
371 | mul(lambda x: add(pprint.pprint, x, 1), 2, 3)
372 | 
373 | 374 |
375 |
376 |

函数组件和返回值

377 |

函数为什么要用栈来描述层级调用关系?

378 |
    379 |
  • 因为函数必须等待返回值或者同步顺序。
  • 380 |
  • 因此调用者的状态需要被保存,换入被调用者执行,直到返回时再换回。
  • 381 |
  • 这个角度来说,调用是最朴素而原始的上下文切换手段。
  • 382 |
  • 无需同步的调用可以用mq来解藕。
  • 383 |
  • 而CPS则象征另一个极端。函数的返回值可以不返回调用者,而是第三者。
  • 384 |
385 | 386 |
387 |
388 |
389 |
390 |
391 |

Q&A

392 | 393 |
394 |
395 |
396 | 397 | 398 | 424 | 425 | 426 | -------------------------------------------------------------------------------- /context/context_slide.md: -------------------------------------------------------------------------------- 1 | # 上下文切换技术简介 2 | 3 | ## logo 4 | 5 | ![PyConChina2014-stick-logo.png](PyConChina2014-stick-logo.png) 6 | 7 | ## 自我介绍 8 | 9 | shell909090,七牛程序员,主要用python和golang。 10 | 11 | ## LICENSE 12 | 13 | [cc-by-sa3.0](http://creativecommons.org/licenses/by-sa/3.0/deed.zh) 14 | 15 | # 系统级上下文技术 16 | 17 | ## 术语 18 | 19 | * 上下文 20 | * 调用栈 21 | * 指向栈的寄存器 22 | * 切换 23 | * 当前执行的一个上下文变为另一个上下文 24 | * 不是上下文的创建和消灭,而是上下文获得CPU时间 25 | * 调度 26 | * 决定哪个上下文应当获得CPU了 27 | * 进程 28 | * 分配资源的单位 29 | * 线程 30 | * 分配CPU的单位 31 | 32 | ## 进程状态 33 | 34 | * 运行: 得到CPU 35 | * 就绪: 可以得到CPU,但是尚未调度到 36 | * 睡眠: 等待某些条件,因此获得了CPU也没用 37 | 38 | ## 性能 39 | 40 | * fork的开销在40-50us不等 41 | * pthread(nptl)的开销在9.5us左右 42 | 43 | # 传统网络服务模型 44 | 45 | ## 如何工作 46 | 47 | 1. 父进程监听服务端口 48 | 2. 在有新连接建立的时候,父进程执行fork,产生一个子进程副本 49 | 3. 如果子进程需要的话,可以exec(例如CGI) 50 | 4. 父进程执行到accept后,发生阻塞 51 | 5. 上下文调度,内核调度器选择下一个上下文,一般就是刚派生的子进程 52 | 6. 子进程进程进入读取处理状态,阻塞在read调用上,所有上下文均进入睡眠态 53 | 7. 随着SYN或者数据报文到来,CPU会唤醒对应fd上阻塞的上下文 54 | 8. 上下文切换到就绪态,并加入调度队列 55 | 9. 继续执行到下一个阻塞调用,或者因为时间片耗尽被挂起 56 | 57 | ## fork的细节 58 | 59 | 产生当前进程的同等副本,在fork时并不立刻分配新的资源。 60 | 61 | 而是在其中某个进程执行了写入时发生复制,这叫做COW。 62 | 63 | 理论上fork应当先执行子进程,因为有很大可能执行exec。 64 | 65 | 而exec执行的快可以避免COW。 66 | 67 | ## 阻塞-调度的细节 68 | 69 | * 当试图读取数据而无数据可读时,既不能得到数据也不能返回。 70 | * 此时上下文处于等待某些条件的状态。 71 | * 在此种条件下,上下文不参与调度。系统会自动选择一个就绪的上下文去切换。 72 | * 当前上下文被挂入等待条件的wait_queue队列。 73 | 74 | * 当条件就绪,例如收到网络数据的时候,会唤醒wait_queue上挂起的所有上下文。 75 | * 不能只唤醒一个。 76 | * 因为如果一个上下文不能消费所有数据,会使得剩余上下文无谓阻塞。 77 | * 但是如果数据只够一个上下文消费,会发生惊群。 78 | 79 | ## 评价 80 | 81 | * 同步模型编写自然,每次读取数据可以当作必然能读取到。 82 | * 进程模型隔离性好,每个上下文可以当作其他上下文不存在一样的操作。 83 | * 即使程序复杂且易崩溃,也只影响一个连接而不是在整个系统。 84 | * 生成和释放开销很大,需要考虑复用。 85 | * 进程模式的多客户通讯比较麻烦,尤其在共享大量数据的时候。 86 | 87 | # C10K问题 88 | 89 | ## 上下文重复建立开销 90 | 91 | 通过上下文池和复用上下文解决。 92 | 93 | 上下文不销毁,而是从头开始循环,获得下一个请求并处理。 94 | 95 | ## 线程复用模式的问题 96 | 97 | 线程复用模式仍然不够快,往往会被认为以下两个因素。 98 | 99 | * 内存 100 | * 内核陷入 101 | 102 | ## linux栈分配原理 103 | 104 | linux下只为栈保留地址空间,而不映射内存。 105 | 106 | 当地址空间首次被访问时,才分配物理内存。因此大量线程不会消耗无谓内存。 107 | 108 | 8M是最大内存限制。 109 | 110 | 当栈回退时,物理页面映射不释放。 111 | 112 | ## 内核陷入开销 113 | 114 | 很小,最低只有50ns。而普通函数调用也有2ns。 115 | 116 | 而且现代高效模型也要通过read陷入来获得数据,这个开销未能避免。 117 | 118 | ## 内核调度器 119 | 120 | 1. linux2.4的调度器。 121 | 2. O(1)调度器。 122 | 3. CFS。 123 | 124 | ## 开销 125 | 126 | yield每次耗费的时间随活跃线程数变化曲线 127 | 128 | ![t_yield.png](t_yield.png) 129 | 130 | --- 131 | 132 | lock每次耗费的时间随活跃线程书变化曲线 133 | 134 | ![t_lock.png](t_lock.png) 135 | 136 | ## lock消耗为什么不随着活跃线程数上升 137 | 138 | 因为lock调用futex,而将线程至于睡眠态。 139 | 140 | # 多路复用 141 | 142 | ## 简述 143 | 144 | * 要突破C10K,就要减少活跃上下文数。因此一个上下文必须能处理多个连接。 145 | * 因此系统调用时必须立刻返回。否则会塞住上下文,阻碍复用。 146 | * 非阻塞调用可以做到立刻返回,但是如何选择要读的文件句柄? 147 | 148 | ## 就绪通知 vs 异步IO 149 | 150 | * 由用户读取 vs 由系统回调 151 | * 在数据就绪时通知 vs 在数据IO完成后回调 152 | 153 | ## linux的就绪通知发展 154 | 155 | * select: 每次调用需要传入fd数组,有最大限制 156 | * poll: 每次调用需要传入fd数组,无最大限制 157 | * epoll: 每次调用无需传入数组,没有最大限制 158 | 159 | ## epoll 160 | 161 | * ET: 状态转变时触发 162 | * LT: 读取所有就绪句柄 163 | 164 | ## 性能分析 165 | 166 | * epoll_wait/ep\_poll\_callback: O(1)级 167 | * epoll_ctl: O(logn)级 168 | 169 | ## 固有缺陷 170 | 171 | 无法用于普通文件。 172 | 173 | # 事件通知机制下的程序设计模型 174 | 175 | ## 用户态调度 176 | 177 | 知道了哪些fd就绪,如何处理这些fd? 178 | 179 | 将对应fd映射到处理这个fd的某组过程上的结构设计,被称为用户态调度。 180 | 181 | 这样,当我们知道某个fd就绪时,可以激活对应的过程。 182 | 183 | 更广义的说,各种内核态带有wait\_queue的对象,在用户态调度上都要做类似处理。 184 | 185 | 例如时钟和锁。 186 | 187 | --- 188 | 189 | 因此用户态调度可类比于内核态调度,但是用户态调度没有抢占。 190 | 191 | 当这个过程长期执行不返回时,其他句柄不能被及时处理。 192 | 193 | 用公平性换取效率。 194 | 195 | ## coroutine 196 | 197 | 切换机器状态字和执行栈不需要进入内核,这也是用户态上下文切换的核心思想。 198 | 199 | setjmp/longjmp只能切换状态字,而没有独立的执行栈。 200 | 201 | 因此setjmp/longjmp用于coroutine总是有点问题,建议用ucontext。 202 | 203 | greenlet也是利用新建独立执行栈和替换当前栈顶部frame的方法来切换。 204 | 205 | ## 协程和线程的关系和区别 206 | 207 | * 协程要获得CPU,必须在线程中执行 208 | * 协程无法跨进程调度 209 | * 同时执行的协程数不能大于容纳他的线程数 210 | * 协程没有抢占 211 | 212 | 因此,单线程中执行的协程,可以视为单线程应用。 213 | 214 | 在阻塞调用之间,访问数据对象时是不需要加锁的。 215 | 216 | ## golang协程性能 217 | 218 | sched调用延迟随goroutine数变化 219 | 220 | ![g_sched.png](g_sched.png) 221 | 222 | --- 223 | 224 | chan调用延迟随goroutine数变化 225 | 226 | ![g_chan.png](g_chan.png) 227 | 228 | --- 229 | 230 | lock调用延迟随goroutine数变化 231 | 232 | ![g_lock.png](g_lock.png) 233 | 234 | ## python协程性能 235 | 236 | * yield的开销大约是22ns 237 | * greenlet的开销大约是500ns 238 | 239 | --- 240 | 241 | yield from随层数变化 242 | 243 | ![py_yieldfrom.png](py_yieldfrom.png) 244 | 245 | ## 基于就绪通知的协程框架 246 | 247 | 1. 包装read/write,检查返回。 248 | 2. 如果是EAGAIN,将当前协程绑定到fd上,然后执行调度函数。 249 | 2. 调度函数epoll,读一个就绪的fd。如果没有,阻塞直到至少一个fd就绪。 250 | 3. 查找这个fd对应的协程上下文对象,并切换过去。 251 | 4. 当某个协程被切换到时,应当再重试读取,失败的话重复2。 252 | 5. 如果读取到数据了,返回。 253 | 254 | --- 255 | 256 | 这样,异步的数据读写动作,在我们的想像中就可以变为同步的。 257 | 258 | 而同步模型会极大降低我们的编程负担。 259 | 260 | ## 回调模型 261 | 262 | 所谓回调模型就是,在IO调用的时候,同时传入一个函数,作为返回函数。 263 | 264 | 当IO结束时,调用传入的函数来处理下面的流程。 265 | 266 | ## CPS 267 | 268 | 用一句话来描述CPS——他把一切操作都当作了IO。 269 | 270 | 无论干什么,结果要通过回调函数来返回。 271 | 272 | 从这个角度来说,IO回调模型只能被视作CPS的一个特例。 273 | 274 | add = lambda f, *nums: f(sum(nums)) 275 | mul = lambda f, *nums: f(reduce(lambda x,y: x*y, nums)) 276 | mul(lambda x: add(pprint.pprint, x, 1), 2, 3) 277 | 278 | ## 函数组件和返回值 279 | 280 | 函数为什么要用栈来描述层级调用关系? 281 | 282 | * 因为函数必须等待返回值或者同步顺序。 283 | * 因此调用者的状态需要被保存,换入被调用者执行,直到返回时再换回。 284 | * 这个角度来说,调用是最朴素而原始的上下文切换手段。 285 | * 无需同步的调用可以用mq来解藕。 286 | * 而CPS则象征另一个极端。函数的返回值可以不返回调用者,而是第三者。 287 | 288 | # Q&A 289 | -------------------------------------------------------------------------------- /context/context_test.md: -------------------------------------------------------------------------------- 1 | # 效率测试 2 | 3 | ## 测试环境 4 | 5 | * Intel(R) Pentium(R) CPU G2030 @ 3.00GHz 6 | * 8G内存 7 | * debian jessie 8 | * Linux 3.16-2-amd64 9 | * 2014年10月27日 10 | 11 | 附注一下,该CPU有2核心,无HT,1ns3个时钟周期。 12 | 13 | ## 测试方法 14 | 15 | 测试代码如下: 16 | 17 | time -f "%e,%S,%c,%r,%s,%K,%P" ./perf_fork 18 | 19 | 数据的意义分别为: 总时间,内核CPU时间,context switch次数,读/写次数,内存耗用,CPU使用百分比。 20 | 21 | 数据处理方法如下: 22 | 23 | import numpy as np 24 | p = lambda s: [float(line.strip().split(',')[0]) for line in s.splitlines()] 25 | q = lambda s: [float(line.strip().split(',')[1]) for line in s.splitlines()] 26 | np.array(p(s)).mean() 27 | np.array(p(s)).var() 28 | np.array(q(s)).mean() 29 | np.array(q(s)).var() 30 | 31 | # 基础开销测试 32 | 33 | ## 函数调用开销 34 | 35 | 使用[s_call](https://gitcafe.com/shell909090/context/blob/master/s_call.c)来测试性能,循环1G次。 36 | 37 | 2.35,0.00,17,0,0,0,99% 38 | 2.34,0.00,13,0,0,0,99% 39 | 2.34,0.00,10,0,0,0,100% 40 | 2.35,0.00,10,0,0,0,99% 41 | 2.34,0.00,14,0,0,0,99% 42 | 2.34,0.00,6,0,0,0,99% 43 | 44 | 统计结果如下: 45 | 46 | * time mean = 2.34 47 | * time var = 0.000022 48 | 49 | 每次call的开销为2.34ns,约7个指令周期。当然,这些并没有考虑调用压栈和数据返回。 50 | 51 | ## 内核调用开销 52 | 53 | 使用[s_syscall](https://gitcafe.com/shell909090/context/blob/master/s_syscall.c)来测试性能,循环1G次。这里特意选用了一个不可能失败的内核函数,getpid,来衡量每次进入getpid的开销。 54 | 55 | 49.61,28.72,891,0,0,0,99% 56 | 49.12,28.57,3582,0,0,0,99% 57 | 49.21,29.66,5813,0,0,0,99% 58 | 49.81,31.04,6076,0,0,0,99% 59 | 49.48,28.59,749,0,0,0,99% 60 | 49.63,29.76,6224,0,0,0,99% 61 | 62 | 统计结果如下: 63 | 64 | * time mean = 49.47 65 | * time var = 0.0585 66 | 67 | 这里可以看到,单次内核陷入的开销在50ns左右,约150个指令周期。内核进入开销比函数调用开销大了一个数量级不止,大约20倍左右。 68 | 69 | # 系统上下文测试 70 | 71 | ## 进程fork开销 72 | 73 | 使用[s_fork](https://gitcafe.com/shell909090/context/blob/master/s_fork.c)程序(注释语句关闭模式),循环1M次,重复6次,原始数据如下: 74 | 75 | 49.04,26.83,29784,0,0,0,55% 76 | 51.53,26.38,32057,0,0,0,52% 77 | 49.88,26.02,30892,0,0,0,53% 78 | 51.39,27.13,37573,0,0,0,54% 79 | 52.89,28.12,37924,0,0,0,54% 80 | 51.19,27.02,35880,0,0,0,54% 81 | 82 | 统计结果如下: 83 | 84 | * time mean = 50.98 85 | * time var = 1.52 86 | * kernel mean = 26.92 87 | * kernel var = 0.43 88 | 89 | 从数据上,我们可以简单得到结论。在测试设备上,每次fork的开销为51us,内核开销为27us,精确级别在1-2us左右。粗略换算一下,一次fork大约消耗了150k个时钟周期。 90 | 91 | 注意,这个数据并不代表fork本身的速度。因为除去fork之外,我们还有子进程退出的开销,父进程wait的开销。甚至严格来说,还包括了至少一次的context switch(有趣的是,这个取决于fork后是优先执行子进程还是父进程)。 92 | 93 | 但是作为进程模式的服务程序,这些开销都是预料中必须付出的。不过我们并没有模拟signal对性能的影响(简单测试表明,性能也会显著的变差)。 94 | 95 | 另外cs次数比产生的进程数远小,可能被第二颗核心执行掉了部分。看来要做精确的测试需要使用单核处理器。 96 | 97 | ## fork模式强制优先执行子进程 98 | 99 | 在[s_fork](https://gitcafe.com/shell909090/context/blob/master/s_fork.c)中,注意那句注释。当优先执行子进程时,会发生什么现象? 100 | 101 | 预期来说,应当不发生变化,或者轻微的变慢。因为我们预期系统优先执行子进程(以减少exec前的page cow)。如果发生变化,那么说明这个假定是不正确的。真实情况是优先执行父进程或者无保证。 102 | 103 | 如果发生变化,首先是一次context switch会变为两次。因为如果在产生了大量子进程后再依次cs,那么需要N+1次cs来结束所有子进程并返回父进程,平均每个子进程一次cs(N足够大的情况下基本近似,例如在标准配置下30000以上)。而如果每次产生子进程就切换,那么会变为每个子进程两次cs。 104 | 105 | 其次,先执行父进程导致在每次调度时的活跃进程数更高,因此调度器的每次执行开销更高。按照算法量级估计,大约是4倍以上。但是实际复杂度的估量比平均值更加麻烦——因为活跃数总是在不停的变化中。大约是Sum(logn)/n=log(n!)/n。因此,虽然在cs次数上减少,但是每次cs的开销会增加。 106 | 107 | 最后,先执行子进程会导致上下文描述符表项被频繁的重用,从而提高命中率。当然,在我们的测试程序中做不到这点,因为每次都是开满才开始回收的。因为直接回收会导致父进程等待子进程结束,从而导致缓慢。所以至少应当需要执行相当的数量才进行回收。 108 | 109 | 而我们摒弃了使用信号响应的方式进行回收,因为信号的速度比fork更慢,这会导致测试不准。 110 | 111 | 下面是实际原始数据: 112 | 113 | 45.19,22.42,399890,0,0,0,51% 114 | 47.66,22.46,414808,0,0,0,48% 115 | 45.51,23.12,376053,0,0,0,52% 116 | 46.35,22.10,401536,0,0,0,49% 117 | 48.28,22.82,415162,0,0,0,48% 118 | 47.44,22.34,413285,0,0,0,48% 119 | 120 | 统计结果如下: 121 | 122 | * time mean = 46.73 123 | * time var = 1.29 124 | * kernel mean = 22.54 125 | * kernel var = 0.11 126 | 127 | 解读上可以发现,每10次fork产生四次cs(这可能是因为第二颗核心执行了部分的子进程),但是每次fork的开销降低为47us,内核CPU降为23us(用户态时间几乎不发生变化),精确级别在1us左右。 128 | 129 | 这说明真实情况确实是先执行父进程或无保证。 130 | 131 | ## 线程建立销毁开销 132 | 133 | 使用[t_thread](https://gitcafe.com/shell909090/context/blob/master/t_thread.c)程序,循环1M次,重复6次,原始数据如下: 134 | 135 | 9.57,8.22,21098,0,0,0,104% 136 | 9.77,8.40,29704,0,0,0,104% 137 | 9.36,8.17,10390,0,0,0,106% 138 | 9.56,8.50,14514,0,0,0,107% 139 | 9.35,8.34,7244,0,0,0,108% 140 | 9.57,8.43,26351,0,0,0,106% 141 | 142 | 统计结果如下: 143 | 144 | * time mean = 9.53 145 | * time var = 0.02 146 | * kernel mean = 8.34 147 | * kernel var = 0.013 148 | 149 | 解读数据可以看到,thread模式的开销为9530ns(已经降到纳秒级了),CPU将为8340ns,精确级别在20ns级别。粗略换算一下每次create的开销大约是30k个时钟周期。简单对比可以看出,thread模式比fork模式大约快了5倍。 150 | 151 | ## 纯线程切换开销 152 | 153 | 使用[t_yield.c](https://gitcafe.com/shell909090/context/blob/master/t_yield.c)程序研究线程切换开销。具体过程单独整理了一个文件,下面只贴出结论: 154 | 155 | ![t_yield.png](t_yield.png) 156 | 157 | 首先注意到一点,由于内核陷入开销的存在,因此这里所有数据都需要扣除50ns。因此图像需要向下移动50ns的距离。 158 | 159 | 基本可以看到,随着上下文数的增加,每次调度开销是随之增加的。而且两者近似的呈一条直线(虽然看起来比较扭曲)。这证明调度复杂度为O(logm)的推论基本是正确的。 160 | 161 | 另外,在1-2线程时性能特别优秀。这可能是线程数小于核数,导致大部分情况下根本未执行调度。对此作出的一点推论是。很多时候,我们为了充分利用CPU会作出worker = N+1的设定。如果IO并不是特别密集,可能性能反而比worker = N更差。 162 | 163 | ## sleep切换开销 164 | 165 | 使用[t_sleep.c](https://gitcafe.com/shell909090/context/blob/master/t_sleep.c)程序来测试usleep(0)的性能。100线程,1M次循环。下面是结论: 166 | 167 | 66.55,90.62,4160,0,0,0,149% 168 | 65.60,87.08,3238,0,0,0,145% 169 | 65.28,83.95,2570,0,0,0,139% 170 | 65.59,85.98,2366,0,0,0,143% 171 | 65.67,86.79,1715,0,0,0,143% 172 | 65.84,87.70,1896,0,0,0,145% 173 | 174 | 统计结果如下: 175 | 176 | * time mean = 65.75 177 | * time var = 0.1539 178 | 179 | 计算结果来说,1315ns。比同样的单纯调度,开销增加了916.8ns。但是在此期间请特别留意CPU利用率曲线。该曲线呈现出明显的先达峰后下降趋势。说明某些线程的sleep比另一些线程的优先得到调度,导致最后CPU利用率不足。从循环次数和下降趋势来说,很难说调度是“近似公平”的。 180 | 181 | 另外特别注意,我们针对1线程1M次循环得到的结论。 182 | 183 | 53.81,2.52,20,0,0,0,4% 184 | 53.88,2.47,22,0,0,0,4% 185 | 53.79,2.48,16,0,0,0,4% 186 | 55.09,2.35,49,0,0,0,4% 187 | 56.19,2.27,50,0,0,0,4% 188 | 55.71,2.43,62,0,0,0,4% 189 | 190 | 统计结果如下: 191 | 192 | * time mean = 54.74 193 | * time var = 0.945 194 | 195 | 这个结果表明,usleep后会导致很长(50us以上)的延迟,在这个时间段内不会有CPU消耗,哪怕usleep(0)。这是由于任务被标记为TASK_INTERRUPTIBLE,然后挂到timer队列上。timer队列调度是有频率限制的,低于这个时间片的sleep不会得到及时响应。这导致usleep测试CPU和调度性能实际上是个很没意义的行为。 196 | 197 | ## lock切换开销 198 | 199 | 使用[t_lock.c](https://gitcafe.com/shell909090/context/blob/master/t_lock.c)来研究pthread mutex的性能。 200 | 201 | ![t_lock.png](t_lock.png) 202 | 203 | 可以看到,mutex几乎不随着并发数的上升而上升。难道是我们的推论有误? 204 | 205 | 我详细的看了下源码。在glibc中,使用的是futex而非mutex实现的锁。前者会把系统置于TASK\_UNINTERRUPTIBLE而后者会置于TASK\_INTERRUPTIBLE。在ps中一望便知。而TASK\_INTERRUPTIBLE是未就绪状态,并不在调度范围内。因此,仅仅在某个上下文执行了unlock后,未进入lock时的短暂时间内,系统有两个可调度上下文。在进入lock后,系统又变为一个可调度上下文。因此,系统在大多数时候都在1-2个可调度上下文间浮动。 206 | 207 | 而且请注意,这里还是换出-换入两次(所有锁类代码都是这样)。 208 | 209 | 而read/write中,活跃上下文数是受客户影响的。当某个fd收到用户数据的时候,对应上下文就会从TASK\_INTERRUPTIBLE转变为就绪,而且在下一个阻塞(或处理完成)前始终占据调度队列。 210 | 211 | # python性能测试 212 | 213 | ## yield模式性能测试 214 | 215 | python下的测试就不用time了,我们改用python的timeit,循环100M次。具体可以看[py_yield.py](https://gitcafe.com/shell909090/context/blob/master/py_yield.py)。数据结果如下: 216 | 217 | 7.64262938499 9.2919304393e-06 218 | 5.41777145863 4.94284924931e-06 219 | 220 | 从结果来看,100M次循环的平均时间是5.4s,平均每次大约54ns。使用yield后变为76ns,增加了22ns。 221 | 222 | ## greenlet模式性能测试 223 | 224 | 这次代码在[py_greenlet.py](https://gitcafe.com/shell909090/context/blob/master/py_greenlet.py),循环10M次。数据结果如下: 225 | 226 | 5.35270996888 7.44085846125e-05 227 | 5.31448976199 5.82336765673e-05 228 | 229 | 单次循环时间消耗为535ns。比最初的54ns,增加了481ns。基本来说,时间增长了10倍率。 230 | 231 | 这是预料中的,因为greenlet早就声明自己通过堆栈拷贝来实现上下文切换。这会消耗大量CPU时间。从原理上说,栈越深,消耗越大。但是测试结果表明两者几乎没有差异,栈深反而性能更加优异。 232 | 233 | ## yield from性能测试 234 | 235 | 这次代码在[py_yield_from.py](https://gitcafe.com/shell909090/context/blob/master/py_yield_from.py),循环100M次。数据结果如下: 236 | 237 | 11.3753386545 2.05564687993e-05 238 | 8.83233247434 7.92599097725e-05 239 | 6.31597025133 0.00346735863271 240 | 241 | 单次循环时间消耗为113.7ns。比最初的63.2ns,增加了50.5ns。单纯的yield为88.3ns,增加了25.1ns。 242 | 243 | yield from需要在python3下执行,从数据上看,python3下执行循环加的过程比python2下更缓慢,而且yield也同样比python2下的缓慢(这也是python3经常被诟病的一个问题,经常比python2慢)。 244 | 245 | 但是针对2/3速度对比问题,我特别声明一下。这个测试没有针对2/3速度对比做定制,也没有经过精细分析。所以不能保证其他人在其他环境下执行出相反的结果。总之,目前我可以做出的简单结论是。python3和python2速度相仿,python2略快。 246 | 247 | 而yield from比yield更慢,这完全可以理解。在python2中,yield from需要用for来表达,这样的结果一个是难看,一个也未必快过yield from。 248 | 249 | 下面我们想要知道一下,随着yield的深度增加,速度是否减慢。用同一个程序里的test_all代码进行了简单的测试后,结果如下: 250 | 251 | ![py_yield_from.png](py_yield_from.png) 252 | 253 | 结论是,随着递归深度的增加,yield from的上下文切换速度是会减慢的。两者呈明显的线性关系。 254 | 255 | # golang性能测试 256 | 257 | ## goroutine创建开销 258 | 259 | 下面是一组golang有关的测试,首先使用[g_goroutine.go](https://gitcafe.com/shell909090/context/blob/master/g_goroutine.go)来测试goroutine创建和销毁开销,执行10M次: 260 | 261 | 5.70,0.11,598,0,0,0,99% 262 | 5.70,0.12,77,0,0,0,99% 263 | 5.66,0.14,334,0,0,0,99% 264 | 5.66,0.10,271,0,0,0,99% 265 | 5.57,0.16,47,0,0,0,99% 266 | 5.77,0.10,521,0,0,0,99% 267 | 268 | 统计结果如下: 269 | 270 | * time mean = 5.68 271 | * time var = 0.0036 272 | 273 | 大致结果为创建开销568ns,大约是线程创建开销的1/20。注意代码没有保证goroutine执行完毕,所以可能会有一定误差。 274 | 275 | ## 调度开销 276 | 277 | 我们使用[g_sched.go](https://gitcafe.com/shell909090/context/blob/master/g_sched.go)来研究golang的调度。 278 | 279 | ![g_sched.png](g_sched.png) 280 | 281 | 可以看到,在很大范围内,延迟虽然随着并发升高而升高,但是升高的幅度并不大。即使是16K这个级别的并发,也只有250ns左右的延迟而已。 282 | 283 | ## chan开销 284 | 285 | 我们使用[g_chan.go](https://gitcafe.com/shell909090/context/blob/master/g_chan.go)来研究chan上的调度。 286 | 287 | ![g_chan.png](g_chan.png) 288 | 289 | 可以看到,chan的性能更加优秀,而且不随着并发的增长而增长,在所有的范围内基本都是60-70ns。这主要是因为chan并不需要“调度”,而只需要上下文切换。所以这个切换是非公平的。chan唤醒上下文的顺序并不一定按照在chan上阻塞的顺序。 290 | 291 | ## lock开销 292 | 293 | 我们使用[g_lock.go](https://gitcafe.com/shell909090/context/blob/master/g_lock.go)来研究chan上的调度。 294 | 295 | ![g_lock.png](g_lock.png) 296 | 297 | 让我颇为困惑的是,lock的性能居然比chan更优秀。在初始阶段都是30ns多点,在256并发上下遇到了剧烈的波动,以上就减少到了20ns。原理上说,lock同样也是不做出顺序假定,无调度的。但是这并不能说明为什么chan比lock性能更差。也许是chan内部做了一些额外工作。而且这些工作应当和并发无关,也就是局部(local)工作。 298 | 299 | # C语言上下文调度 300 | 301 | ## setjmp/longjmp测试 302 | 303 | 使用[s_jmp](https://gitcafe.com/shell909090/context/blob/master/s_jmp.c)程序来测试setjmp的性能。1G次循环。下面是结论: 304 | 305 | 5.77,0.00,29,0,0,0,99% 306 | 5.70,0.00,30,0,0,0,99% 307 | 5.71,0.00,22,0,0,0,99% 308 | 5.71,0.00,23,0,0,0,99% 309 | 5.70,0.00,30,0,0,0,100% 310 | 5.70,0.00,23,0,0,0,99% 311 | 312 | 统计结果如下: 313 | 314 | * time mean = 5.715 315 | * time var = 0.000625 316 | 317 | 单次调度开销只有5.7ns,在所有测试中性能最优。(glibc-2.19/sysdeps/x86_64/setjmp.S) 318 | 319 | ## makecontext/swapcontext测试 320 | 321 | 使用[s_context](https://gitcafe.com/shell909090/context/blob/master/s_context.c)程序来测试ucontext的性能。100M次循环。下面是结论: 322 | 323 | 33.42,13.08,163,0,0,0,99% 324 | 33.43,13.40,147,0,0,0,99% 325 | 33.39,13.24,143,0,0,0,99% 326 | 33.41,13.25,156,0,0,0,99% 327 | 33.70,13.41,328,0,0,0,99% 328 | 33.42,13.18,290,0,0,0,99% 329 | 330 | 统计结果如下: 331 | 332 | * time mean = 33.46 333 | * time var = 0.0115 334 | 335 | 单次调度开销高达167.3ns,仅比系统的sched在高线程下略快。这事很奇怪,因为根据我看到的源码(glibc-2.19/sysdeps/unix/sysv/linux/x86_64/setcontext.S),getcontext/setcontext在glibc中是用汇编实现的。其中陷入内核只是为了设定signal mask。 336 | -------------------------------------------------------------------------------- /context/g_chan.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shell909090/slides/d8aa123521daedee78e85e06f80c621929afaebd/context/g_chan.png -------------------------------------------------------------------------------- /context/g_chan.txt: -------------------------------------------------------------------------------- 1 | 1, 64.113970, 0.006184 2 | 2, 64.928007, 0.000422 3 | 4, 64.851443, 0.003175 4 | 8, 64.857492, 0.000842 5 | 16, 64.695029, 0.008031 6 | 32, 64.825123, 0.000831 7 | 64, 64.873588, 0.000521 8 | 128, 64.889541, 0.000812 9 | 256, 67.017225, 0.012074 10 | 512, 66.455753, 0.018068 11 | 1024, 66.181475, 0.019556 12 | 2048, 65.057419, 0.008656 13 | 4096, 63.506043, 0.004279 14 | 8192, 62.628012, 0.000281 15 | 16384, 62.338620, 0.001394 16 | 32768, 62.220726, 0.000235 17 | 65536, 41.430498, 0.000084 18 | -------------------------------------------------------------------------------- /context/g_lock.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shell909090/slides/d8aa123521daedee78e85e06f80c621929afaebd/context/g_lock.png -------------------------------------------------------------------------------- /context/g_lock.txt: -------------------------------------------------------------------------------- 1 | 1, 21.120755, 0.002262 2 | 2, 27.821820, 0.132264 3 | 4, 33.612592, 0.045415 4 | 8, 34.423486, 0.019039 5 | 16, 35.118043, 0.008278 6 | 32, 35.592795, 0.001518 7 | 64, 35.197076, 0.008019 8 | 128, 35.317440, 0.005391 9 | 256, 24.340722, 2.483812 10 | 512, 21.735521, 0.003531 11 | 1024, 21.661740, 0.005824 12 | 2048, 21.756460, 0.002096 13 | 4096, 21.684911, 0.001201 14 | 8192, 21.392722, 0.000164 15 | 16384, 21.101328, 0.000061 16 | 32768, 20.981675, 0.000074 17 | 65536, 13.932590, 0.000056 18 | -------------------------------------------------------------------------------- /context/g_sched.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shell909090/slides/d8aa123521daedee78e85e06f80c621929afaebd/context/g_sched.png -------------------------------------------------------------------------------- /context/g_sched.txt: -------------------------------------------------------------------------------- 1 | 1, 99.496343, 0.009872 2 | 2, 89.269159, 0.016412 3 | 4, 86.859009, 0.000353 4 | 8, 88.319799, 0.026965 5 | 16, 87.526381, 0.014797 6 | 32, 88.434150, 0.005842 7 | 64, 92.272177, 0.004080 8 | 128, 97.510379, 0.000727 9 | 256, 99.786377, 0.005801 10 | 512, 117.691595, 0.136340 11 | 1024, 144.991233, 0.005184 12 | 2048, 162.781891, 0.081429 13 | 4096, 166.832305, 0.065160 14 | 8192, 195.319440, 6.487632 15 | 16384, 238.133483, 39.085366 16 | 32768, 293.547568, 41.131047 17 | 65536, 216.655828, 3.289559 18 | -------------------------------------------------------------------------------- /context/py_yieldfrom.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shell909090/slides/d8aa123521daedee78e85e06f80c621929afaebd/context/py_yieldfrom.png -------------------------------------------------------------------------------- /context/py_yieldfrom.txt: -------------------------------------------------------------------------------- 1 | 1, 0.113695, 0.000000 2 | 3, 0.180671, 0.000000 3 | 5, 0.231601, 0.000001 4 | 7, 0.307764, 0.000003 5 | 9, 0.371587, 0.000001 6 | 11, 0.421375, 0.000001 7 | 13, 0.475800, 0.000003 8 | 15, 0.530220, 0.000002 9 | 17, 0.594413, 0.000002 10 | 19, 0.645104, 0.000002 11 | 21, 0.704012, 0.000018 12 | 23, 0.761635, 0.000005 13 | 25, 0.826917, 0.000006 14 | 27, 0.880991, 0.000004 15 | 29, 0.952826, 0.000004 16 | -------------------------------------------------------------------------------- /context/t_lock.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shell909090/slides/d8aa123521daedee78e85e06f80c621929afaebd/context/t_lock.png -------------------------------------------------------------------------------- /context/t_lock.txt: -------------------------------------------------------------------------------- 1 | 1, 37.167000, 0.008577 2 | 2, 230.747000, 0.322283 3 | 4, 209.770000, 0.457680 4 | 8, 223.119000, 0.268887 5 | 16, 219.305000, 0.276515 6 | 32, 209.770000, 0.648380 7 | 64, 234.561000, 0.345167 8 | 128, 204.622000, 0.513898 9 | 256, 196.031000, 0.381909 10 | 512, 187.200000, 0.499200 11 | 1024, 177.645000, 1.577355 12 | 2048, 177.898000, 2.386588 13 | 4096, 170.976000, 3.117888 14 | 8192, 166.368000, 10.881984 15 | 16384, 172.872000, 10.897488 16 | -------------------------------------------------------------------------------- /context/t_yield.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shell909090/slides/d8aa123521daedee78e85e06f80c621929afaebd/context/t_yield.png -------------------------------------------------------------------------------- /context/t_yield.txt: -------------------------------------------------------------------------------- 1 | 1, 150.574000, 0.015248 2 | 2, 160.188000, 0.083908 3 | 4, 339.446000, 0.030512 4 | 8, 348.981000, 0.154467 5 | 16, 366.144000, 0.030512 6 | 32, 387.121000, 0.040047 7 | 64, 421.447000, 0.398563 8 | 128, 462.352000, 0.099968 9 | 256, 587.312000, 2.058716 10 | 512, 649.740000, 2.505360 11 | 1024, 722.865000, 5.869695 12 | 2048, 752.041000, 3.104097 13 | 4096, 730.800000, 1.956720 14 | 8192, 753.504000, 9.072096 15 | 16384, 752.628000, 18.968988 16 | -------------------------------------------------------------------------------- /ipynb/learn-python-qrcode.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shell909090/slides/d8aa123521daedee78e85e06f80c621929afaebd/ipynb/learn-python-qrcode.jpg -------------------------------------------------------------------------------- /ipynb/life.csv: -------------------------------------------------------------------------------- 1 | age,CL1,CL2,CL3,CL4,CL5,CL6 2 | 0,0.000867,0.00062,0.00062,0.000455,0.000566,0.000453 3 | 1,0.000615,0.000456,0.000465,0.000324,0.000386,0.000289 4 | 2,0.000445,0.000337,0.000353,0.000236,0.000268,0.000184 5 | 3,0.000339,0.000256,0.000278,0.00018,0.000196,0.000124 6 | 4,0.00028,0.000203,0.000229,0.000149,0.000158,0.000095 7 | 5,0.000251,0.00017,0.0002,0.000131,0.000141,0.000084 8 | 6,0.000237,0.000149,0.000182,0.000119,0.000132,0.000078 9 | 7,0.000233,0.000137,0.000172,0.00011,0.000129,0.000074 10 | 8,0.000238,0.000133,0.000171,0.000105,0.000131,0.000072 11 | 9,0.00025,0.000136,0.000177,0.000103,0.000137,0.000072 12 | 10,0.000269,0.000145,0.000187,0.000103,0.000146,0.000074 13 | 11,0.000293,0.000157,0.000202,0.000105,0.000157,0.000077 14 | 12,0.000319,0.000172,0.00022,0.000109,0.00017,0.00008 15 | 13,0.000347,0.000189,0.00024,0.000115,0.000184,0.000085 16 | 14,0.000375,0.000206,0.000261,0.000121,0.000197,0.00009 17 | 15,0.000402,0.000221,0.00028,0.000128,0.000208,0.000095 18 | 16,0.000427,0.000234,0.000298,0.000135,0.000219,0.0001 19 | 17,0.000449,0.000245,0.000315,0.000141,0.000227,0.000105 20 | 18,0.000469,0.000255,0.000331,0.000149,0.000235,0.00011 21 | 19,0.000489,0.000262,0.000346,0.000156,0.000241,0.000115 22 | 20,0.000508,0.000269,0.000361,0.000163,0.000248,0.00012 23 | 21,0.000527,0.000274,0.000376,0.00017,0.000256,0.000125 24 | 22,0.000547,0.000279,0.000392,0.000178,0.000264,0.000129 25 | 23,0.000568,0.000284,0.000409,0.000185,0.000273,0.000134 26 | 24,0.000591,0.000289,0.000428,0.000192,0.000284,0.000139 27 | 25,0.000615,0.000294,0.000448,0.0002,0.000297,0.000144 28 | 26,0.000644,0.0003,0.000471,0.000208,0.000314,0.000149 29 | 27,0.000675,0.000307,0.000497,0.000216,0.000333,0.000154 30 | 28,0.000711,0.000316,0.000526,0.000225,0.000354,0.00016 31 | 29,0.000751,0.000327,0.000558,0.000235,0.000379,0.000167 32 | 30,0.000797,0.00034,0.000595,0.000247,0.000407,0.000175 33 | 31,0.000847,0.000356,0.000635,0.000261,0.000438,0.000186 34 | 32,0.000903,0.000374,0.000681,0.000277,0.000472,0.000198 35 | 33,0.000966,0.000397,0.000732,0.000297,0.000509,0.000213 36 | 34,0.001035,0.000423,0.000788,0.000319,0.000549,0.000231 37 | 35,0.001111,0.000454,0.00085,0.000346,0.000592,0.000253 38 | 36,0.001196,0.000489,0.000919,0.000376,0.000639,0.000277 39 | 37,0.00129,0.00053,0.000995,0.000411,0.00069,0.000305 40 | 38,0.001395,0.000577,0.001078,0.00045,0.000746,0.000337 41 | 39,0.001515,0.000631,0.00117,0.000494,0.000808,0.000372 42 | 40,0.001651,0.000692,0.00127,0.000542,0.000878,0.00041 43 | 41,0.001804,0.000762,0.00138,0.000595,0.000955,0.00045 44 | 42,0.001978,0.000841,0.0015,0.000653,0.001041,0.000494 45 | 43,0.002173,0.000929,0.001631,0.000715,0.001138,0.00054 46 | 44,0.002393,0.001028,0.001774,0.000783,0.001245,0.000589 47 | 45,0.002639,0.001137,0.001929,0.000857,0.001364,0.00064 48 | 46,0.002913,0.001259,0.002096,0.000935,0.001496,0.000693 49 | 47,0.003213,0.001392,0.002277,0.00102,0.001641,0.00075 50 | 48,0.003538,0.001537,0.002472,0.001112,0.001798,0.000811 51 | 49,0.003884,0.001692,0.002682,0.001212,0.001967,0.000877 52 | 50,0.004249,0.001859,0.002908,0.001321,0.002148,0.00095 53 | 51,0.004633,0.002037,0.00315,0.001439,0.00234,0.001031 54 | 52,0.005032,0.002226,0.003409,0.001568,0.002544,0.00112 55 | 53,0.005445,0.002424,0.003686,0.001709,0.002759,0.001219 56 | 54,0.005869,0.002634,0.003982,0.001861,0.002985,0.001329 57 | 55,0.006302,0.002853,0.004297,0.002027,0.003221,0.00145 58 | 56,0.006747,0.003085,0.004636,0.002208,0.003469,0.001585 59 | 57,0.007227,0.003342,0.004999,0.002403,0.003731,0.001736 60 | 58,0.00777,0.003638,0.005389,0.002613,0.004014,0.001905 61 | 59,0.008403,0.00399,0.005807,0.00284,0.004323,0.002097 62 | 60,0.009161,0.004414,0.006258,0.003088,0.00466,0.002315 63 | 61,0.010065,0.004923,0.006742,0.003366,0.005034,0.002561 64 | 62,0.011129,0.005529,0.007261,0.003684,0.005448,0.002836 65 | 63,0.01236,0.006244,0.007815,0.004055,0.005909,0.003137 66 | 64,0.013771,0.007078,0.008405,0.004495,0.006422,0.003468 67 | 65,0.015379,0.008045,0.009039,0.005016,0.006988,0.003835 68 | 66,0.017212,0.009165,0.009738,0.005626,0.00761,0.004254 69 | 67,0.019304,0.01046,0.010538,0.006326,0.008292,0.00474 70 | 68,0.021691,0.011955,0.011496,0.007115,0.009046,0.005302 71 | 69,0.024411,0.013674,0.012686,0.008,0.009897,0.005943 72 | 70,0.027495,0.015643,0.014192,0.009007,0.010888,0.00666 73 | 71,0.030965,0.017887,0.016106,0.010185,0.01208,0.00746 74 | 72,0.034832,0.020432,0.018517,0.011606,0.01355,0.008369 75 | 73,0.039105,0.023303,0.02151,0.013353,0.015387,0.009436 76 | 74,0.043796,0.026528,0.025151,0.015508,0.017686,0.01073 77 | 75,0.048921,0.030137,0.02949,0.018134,0.020539,0.012332 78 | 76,0.054506,0.034165,0.034545,0.021268,0.024017,0.014315 79 | 77,0.060586,0.038653,0.04031,0.024916,0.028162,0.016734 80 | 78,0.067202,0.043648,0.046747,0.029062,0.032978,0.019619 81 | 79,0.0744,0.049205,0.053801,0.033674,0.038437,0.022971 82 | 80,0.08222,0.055385,0.061403,0.038718,0.044492,0.02677 83 | 81,0.0907,0.062254,0.069485,0.04416,0.051086,0.030989 84 | 82,0.099868,0.06988,0.077987,0.049977,0.058173,0.035598 85 | 83,0.109754,0.07832,0.086872,0.056157,0.065722,0.040576 86 | 84,0.120388,0.087611,0.09613,0.062695,0.073729,0.045915 87 | 85,0.131817,0.097754,0.105786,0.069596,0.082223,0.051616 88 | 86,0.144105,0.108704,0.1159,0.076863,0.091239,0.057646 89 | 87,0.157334,0.120371,0.126569,0.084501,0.1009,0.064084 90 | 88,0.171609,0.132638,0.137917,0.092504,0.111321,0.070942 91 | 89,0.187046,0.145395,0.150089,0.100864,0.122608,0.078241 92 | 90,0.203765,0.158572,0.163239,0.109567,0.13487,0.086003 93 | 91,0.221873,0.172172,0.177519,0.118605,0.148212,0.094249 94 | 92,0.241451,0.186294,0.193067,0.127985,0.162742,0.103002 95 | 93,0.262539,0.201129,0.209999,0.137743,0.178566,0.112281 96 | 94,0.285129,0.21694,0.228394,0.147962,0.195793,0.122109 97 | 95,0.30916,0.234026,0.248299,0.158777,0.214499,0.13254 98 | 96,0.334529,0.252673,0.269718,0.17038,0.23465,0.143757 99 | 97,0.361101,0.273112,0.292621,0.18302,0.25618,0.155979 100 | 98,0.388727,0.295478,0.316951,0.196986,0.279025,0.169421 101 | 99,0.417257,0.319794,0.342628,0.212604,0.30312,0.184301 102 | 100,0.446544,0.345975,0.369561,0.230215,0.328401,0.200836 103 | 101,0.476447,0.373856,0.397652,0.250172,0.354803,0.219242 104 | 102,0.50683,0.403221,0.426801,0.272831,0.382261,0.239737 105 | 103,0.537558,0.433833,0.456906,0.298551,0.41071,0.262537 106 | 104,0.568497,0.465447,0.487867,0.327687,0.440086,0.287859 107 | 105,1,1,1,1,1,1 108 | -------------------------------------------------------------------------------- /ipynb/population.csv: -------------------------------------------------------------------------------- 1 | year,birth,total 2 | 1946,1009, 3 | 1947,1122, 4 | 1948,1139, 5 | 1949,1275,54167 6 | 1950,2023,55196 7 | 1951,2063,56300 8 | 1952,2105,57482 9 | 1953,2215,58796 10 | 1954,2245,60266 11 | 1955,1978,61465 12 | 1956,1976,62828 13 | 1957,2167,64653 14 | 1958,1905,65994 15 | 1959,1647,67207 16 | 1960,1389,66207 17 | 1961,1188,65859 18 | 1962,2460,67296 19 | 1963,2954,69172 20 | 1964,2729,70499 21 | 1965,2704,72538 22 | 1966,2577,74542 23 | 1967,2563,76368 24 | 1968,2757,78534 25 | 1969,2715,80671 26 | 1970,2736,82992 27 | 1971,2567,85229 28 | 1972,2566,87177 29 | 1973,2463,89211 30 | 1974,2235,90859 31 | 1975,2109,92420 32 | 1976,1853,93717 33 | 1977,1787,94974 34 | 1978,1745,96259 35 | 1979,1727,97542 36 | 1980,1779,98705 37 | 1981,2069,100072 38 | 1982,2238,101654 39 | 1983,2058,103008 40 | 1984,2055,104357 41 | 1985,2202,105851 42 | 1986,2384,107507 43 | 1987,2522,109300 44 | 1988,2457,111026 45 | 1989,2407,112704 46 | 1990,2391,114333 47 | 1991,2258,115823 48 | 1992,2119,117171 49 | 1993,2126,118517 50 | 1994,2104,119850 51 | 1995,2063,121121 52 | 1996,2067,122389 53 | 1997,2038,123626 54 | 1998,1942,124761 55 | 1999,1834,125786 56 | 2000,1771,126743 57 | 2001,1702,127627 58 | 2002,1647,128453 59 | 2003,1599,129227 60 | 2004,1593,129988 61 | 2005,1617,130756 62 | 2006,1584,131448 63 | 2007,1594,132129 64 | 2008,1608,133126 65 | 2009,1591,133450 66 | 2010,1592,134091 67 | 2011,1604,134916 68 | 2012,1635,135922 69 | 2013,1640,136726 70 | 2014,1687,137646 71 | 2015,1655,138326 72 | 2016,1786,139232 73 | 2017,1723,140011 74 | 2018,1523,140541 75 | 2019,1465,141008 76 | 2020,1202,141212 77 | 2021,1062,141260 78 | 2022,956,141175 79 | 2023,902,141236 80 | -------------------------------------------------------------------------------- /ipynb/pycon2016.md: -------------------------------------------------------------------------------- 1 | # 程序猿训练师养成计划1.0 2 | 3 | 在开发速度越来越快的今天,大量靠谱的程序猿是必不可少的。如何批量的,靠谱的复制出大量的Python程序猿? 4 | 5 | # 想做的事 6 | 7 | 提高Python使用量,提高Python从业人员水平。 8 | 9 | # 门槛太低 10 | 11 | Python拥有非常好的可读性,使得用户更多的将精力集中在逻辑上而非细节上。 12 | 13 | 我推荐大学生学习计算机的第一门语言学Python。 14 | 15 | 对于非计算机专业的语言选修,也建议选Python而非C。 16 | 17 | --- 18 | 19 | 然而这一切,都使得Python从业人员的水平更低而不是更高。 20 | 21 | # 社群太小 22 | 23 | 有的时候,朋友知道我用Python,让我给他出出主意。我看了一圈情况,沉痛的建议他不要用Python。 24 | 25 | 为什么? 26 | 27 | --- 28 | 29 | 因为他们根本Hold不住Python。 30 | 31 | 招聘的员工,大多都没有任何用过Python的经验。 32 | 33 | --- 34 | 35 | 这往往是个悖论。会用Python的人越少,公司越不敢用。公司越不敢用,会用Python的人越少。 36 | 37 | 这种死循环,严重的影响了Python社群的发展。 38 | 39 | # 门槛降低是未来必然的趋势 40 | 41 | 60年前,计算机刚诞生的时候,程序员都是什么人?70-80年代的黄金时期,程序员都是什么人?按照当时的标准,这里的大部分程序员都是废柴中的废柴。 42 | 43 | 随着时间的流逝,技术的发展。今天,你我这样的废柴也有机会写个程序。来改变自己的家庭,改变他人的生活,甚至改变这个世界。 44 | 45 | --- 46 | 47 | 未来,程序员这个职业甚至可能消失,就像今天的司机一样。驾驶不再仅仅是一种职业,而且是一项技能。人人都会写程序,差别只是你为了别人工作,还是为了解决自己的问题。 48 | 49 | # 要为社区培养足够的程序员 50 | 51 | 只有程序员多,公司才敢用。公司敢用,Python才有未来。 52 | 53 | 同样,也只有程序员多,才能改变Python程序员都是新手的印象。让人了解,Python是一门适合新手的语言,但是也是可以拿来认真做一些事情的。 54 | 55 | # 自学的烦恼 56 | 57 | 经历过自学的人,应该知道。自学会烦恼的是没有老师和社群。而老师和社群最主要的能力,不是教会你如何写代码,而是督促你在学不下去的时候继续,和为你解答困扰了N久的问题。 58 | 59 | # 老师的烦恼 60 | 61 | 很多程序员,也许愿意分享他的技术细节,但是却很难拉出去讲Python课。因为每次讲课,需要备课,准备材料,做演讲时间评估,等等等等。讲课也许没用多久,但是材料准备会花去很久。 62 | 63 | # 程序猿训练师养成计划1.0 64 | 65 | 我准备开放一个Python教学体系,包含多门围绕Python技术的课程。每门课包括slide,演示材料,代码,作业,测试评估。 66 | 67 | 课程内容采用cc-by-sa4.0公布,欢迎大家使用。同时也希望大家根据自身使用的结果,提出修改意见,甚至修改并提出PR。 68 | 69 | # 这和网上其他免费的Python教材有什么不同 70 | 71 | OpenSource和FreeSoftware有什么不同? 72 | 73 | --- 74 | 75 | 我的目标并不是让学生去“学”这本教材,而是让老师“可以教”这本教材。 76 | 77 | 在“可以学”和“可以教”之间,有着巨大的鸿沟。 78 | 79 | --- 80 | 81 | 网上有很多教材,但是我没有找到一本中文材料,授权老师可以用这本教材从事教学的。更准确的说,大部分教材根本没有声明版权和授权。对书中代码进行授权已经算非常不错的了。 82 | 83 | 同时,网上也有很多Python书籍,有明确的版权和授权。但是我在里面找不到一本书,有演示材料,练习,作业,评估之类的教学辅助材料。 84 | 85 | 从学习角度来说,这影响并不是挺大。但是从教课角度来讲,就不能这么轻忽处理。毕竟老师授课,很多时候是基于商业目地运用,已经夠上了侵犯版权的边。 86 | -------------------------------------------------------------------------------- /ipynb/pycon2019.md: -------------------------------------------------------------------------------- 1 | # Abstract 2 | 3 | This article briefly introduced how to make a home bot by Python and IM. Described some function and its circumstances. Could have help to anyone with the same situations. 4 | 5 | # 简介 6 | 7 | 简单介绍了使用Python+IM制作家用bot的过程。列举了一些家用Bot的实用功能和使用场景。可以为有类似情况的朋友提供参考。 8 | 9 | # outline 10 | 11 | * Abstract 12 | * 简介 13 | * 背景 14 | * 路由器设计 15 | * 网络结构 16 | * NAS 17 | * 电视机+机顶盒 18 | * 通知功能 19 | * 通知基础原理 20 | * 为什么不直接使用SMTP 21 | * 通知分级制 22 | * DHCP通知 23 | * VPN接入/断开通知 24 | * NAS访问通知 25 | * 各种监控通知 26 | * 如果路由器不是自己打造的 27 | * 定时 28 | * 命令 29 | * 命令的执行机制 30 | * level 31 | * WOL 32 | * search 33 | * 重启服务 34 | * NATS 35 | * 某些神秘的小功能 36 | -------------------------------------------------------------------------------- /ipynb/wechat-qrcode.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shell909090/slides/d8aa123521daedee78e85e06f80c621929afaebd/ipynb/wechat-qrcode.png -------------------------------------------------------------------------------- /md/apt.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | apt 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 |
17 |
18 |
19 |

dpkg系统

20 | 21 |
22 |
23 |

简述

24 |

用于管理程序安装和删除的基础系统。

25 | 26 |
27 |
28 |

和make install的区别

29 |
    30 |
  1. 可以安装和卸载,并且可以管理依赖性关系。
  2. 31 |
  3. 具有安装和卸载时进行配置管理的能力。
  4. 32 |
  5. 可以被视做带有元信息,解压前后和删除前后会执行脚本的tar包
  6. 33 |
34 | 35 |
36 |
37 |

和apt的区别

38 |
    39 |
  1. 只能管理系统中已经安装的包
  2. 40 |
  3. 不解决依赖性问题,只能报错
  4. 41 |
  5. 不解决可信性问题
  6. 42 |
43 | 44 |
45 |
46 |

列出已安装的包和版本

47 |
dpkg -l
 48 | 
49 | 50 |
51 |
52 |
53 |
54 |
55 |
56 |

dpkg元信息

57 | 58 |
59 |
60 |

包元信息

61 |
    62 |
  1. 包名
  2. 63 |
  3. 版本
  4. 64 |
  5. 架构(arch)
  6. 65 |
  7. 依赖(Depends)
  8. 66 |
  9. 推荐(Recommands)
  10. 67 |
  11. 建议(Suggests)
  12. 68 |
  13. 描述
  14. 69 |
70 | 71 |
72 |
73 |

已安装包的状态

74 |
dpkg -s <package>
 75 | 
76 | 77 |
78 |
79 |

查看包属性

80 |
dpkg -I <debfile>
 81 | 
82 | 83 |
84 |
85 |
86 |
87 |
88 |
89 |

dpkg内容

90 | 91 |
92 |
93 |

包内容

94 |
    95 |
  1. 所包含的文件,大小,属主,权限,日期
  2. 96 |
  3. 安装前后脚本(preinst/postinst)
  4. 97 |
  5. 卸载前后脚本(prerm/postrm)
  6. 98 |
  7. 冲突文件(conffiles)
  8. 99 |
100 | 101 |
102 |
103 |

列出包里面包含的文件

104 |
dpkg -L <package>
105 | 
106 | 107 |
108 |
109 |

查看包内容

110 |
dpkg -c <debfile>
111 | 
112 | 113 |
114 |
115 |
116 |
117 |
118 |
119 |

dpkg配置

120 | 121 |
122 |
123 |

debconf

124 |

用于配置包的交互系统。

125 | 126 |
127 |
128 |

包状态

129 |
    130 |
  1. Status: Not/Inst/Conf-files/Unpacked/halF-conf/Half-inst/trig-aWait/Trig-pend
  2. 131 |
  3. Err: (none)/Reinst-required (Status,Err: uppercase=bad)
  4. 132 |
133 |

状态标明了包在系统中处于哪个阶段。

134 | 135 |
136 |
137 |

安装包

138 |
dpkg -i <debfile>
139 | 
140 | 141 |
142 |
143 |

删除包

144 |
dpkg -r <package>
145 | 
146 |

删除配置

147 |
dpkg -P <package>
148 | 
149 | 150 |
151 |
152 |

配置继续

153 |
dpkg --configure -a
154 | 
155 | 156 |
157 |
158 |
159 |
160 |
161 |
162 |

dpkg杂项

163 | 164 |
165 |
166 |

查找某个文件属于哪个包

167 |
dpkg -S filepath
168 | 
169 | 170 |
171 |
172 |

不安装解开包内容

173 |
dpkg -x <debfile> <target>
174 | 
175 | 176 |
177 |
178 |
179 |
180 |
181 |
182 |

apt系统

183 | 184 |
185 |
186 |

简述

187 |

用于管理程序源的基础系统。

188 | 189 |
190 |
191 |

包列表

192 |

整个系统中所列的所有源里面载明的包列表。

193 |

列表里面有数据,指明某个包可以在哪个url取得,大小多少,md5/sha1/sha256。因此下载列表所指明的文件是不会遭到替换的(碰撞算法不算)。

194 | 195 |
196 |
197 |

更新包列表

198 |
apt-get update
199 | aptitude update
200 | 
201 | 202 |
203 |
204 |

下载包

205 |
apt-get download <package>
206 | aptitude download <package>
207 | 
208 |

下载源码

209 |
apt-get source <package>
210 | aptitude source <package>
211 | 
212 | 213 |
214 |
215 |

安装

216 |
apt-get install <package>
217 | aptitude install <package>
218 | 
219 | 220 |
221 |
222 |

删除

223 |
apt-get remove <package>
224 | aptitude remove <package>
225 | 
226 |

删除带配置

227 |
apt-get purge <package>
228 | aptitude purge <package>
229 | 
230 | 231 |
232 |
233 |
234 |
235 |
236 |
237 |

apt源

238 | 239 |
240 |
241 |

源配置

242 |

/etc/apt/sources.list文件中,每行指明一个源。

243 |
deb http://192.168.1.22:9999/debian/ wheezy main contrib non-free
244 | 
245 |
    246 |
  1. 第一部分指明是二进制源(deb)还是源码源(deb-src)。
  2. 247 |
  3. 第二部分指明基url。
  4. 248 |
  5. 第三部分指明release。
  6. 249 |
  7. 第四部分和后续指明category。
  8. 250 |
251 | 252 |
253 |
254 |

源的三要素

255 |
    256 |
  1. release,指发行版本。例如wheezy/stable, jessie/testing, sid/unstable,或者是precise/lucid等。
  2. 257 |
  3. category,仓库分类。可自行选定的仓库分类,大部分发行上只有一个,即main。但是有补充,例如debian的non-free和ubuntu的restricted。
  4. 258 |
  5. arch,cpu架构。无需指定安装时固定。可以用dpkg --print-architecture查看和管理。debian目前已经支持multiarch,但是效果不好说。
  6. 259 |
260 | 261 |
262 |
263 |

可信赖源

264 |

源给出的包列表使得下载包是安全的,但是包列表本身还可能遭到替换。为了解决这个问题,需要对包列表进行签名。apt使用的是gpg签名系统。在每个系统上,都安装了可信签名公钥。

265 |
sudo apt-key list
266 | 
267 |

凡是经过可信密钥签名的仓库,都是可信仓库。而非可信仓库添加进去的第一件事,就是添加签名公钥。

268 | 269 |
270 |
271 |

自建源缓存

272 |

安装approx,修改/etc/approx/approx.conf

273 |

每行一条,开始指明缓存路径,后续指明上游目标。例如:

274 |
debian          http://mirrors.ustc.edu.cn/debian
275 | ubuntu          http://mirrors.163.com/ubuntu
276 | gplhost         http://mirrors.shell909090.org/gplhost
277 | 
278 |

第一条说明http://server:9999/debian映射到http://mirrors.ustc.edu.cn/debian。后续同。第三条为openstack在wheezy上的backport附加源。

279 | 280 |
281 |
282 |
283 |
284 |
285 |
286 |

依赖求解

287 | 288 |
289 |
290 |

依赖关系图

291 |
    292 |
  1. Depends, Recommands。
  2. 293 |
  3. 依赖版本>=和==。
  4. 294 |
295 | 296 |
297 |
298 |

手工安装和依赖安装

299 |
aptitude search '.*' | grep '^i'
300 | apt-mark showmanual
301 | apt-mark showauto
302 | 
303 | 304 |
305 |
306 |

冲突

307 |
apt-cache show nginx-light
308 | Conflicts: nginx-extras, nginx-full, nginx-naxsi
309 | 
310 |

冲突主要用于解决有相同路径的文件,具有替代功能的程序,或不兼容的版本。

311 | 312 |
313 |
314 |

版本升级

315 |
apt-get upgrade
316 | aptitude upgrade
317 | 
318 |

在版本升级中,很容易出现冲突。例如新包依赖于一个新的库,但是系统中某个未升级的程序依赖于一个老的库。两者都是强制依赖,因此不能同时存在于系统中。

319 | 320 |
321 |
322 |

冲突解决方案

323 |

使用aptitude,进入后可以看到冲突的存在。按E查看冲突解决方案,或调整方案。

324 |

最有效的方法是自行求解。通过自行标记某些包为安装或删除状态,解除冲突。

325 | 326 |
327 |
328 |

只升级安全补丁

329 |
unattended-upgrades
330 | 
331 |

由于升级的量更小,因此几乎不可能碰到冲突。

332 | 333 |
334 |
335 |
336 |
337 | 338 | 339 | 365 | 366 | 367 | -------------------------------------------------------------------------------- /md/apt.md: -------------------------------------------------------------------------------- 1 | # dpkg系统 2 | 3 | ## 简述 4 | 5 | 用于管理程序安装和删除的基础系统。 6 | 7 | ## 和make install的区别 8 | 9 | 1. 可以安装和卸载,并且可以管理依赖性关系。 10 | 2. 具有安装和卸载时进行配置管理的能力。 11 | 3. 可以被视做带有元信息,解压前后和删除前后会执行脚本的tar包 12 | 13 | ## 和apt的区别 14 | 15 | 1. 只能管理系统中已经安装的包 16 | 2. 不解决依赖性问题,只能报错 17 | 3. 不解决可信性问题 18 | 19 | ## 列出已安装的包和版本 20 | 21 | dpkg -l 22 | 23 | # dpkg元信息 24 | 25 | ## 包元信息 26 | 27 | 1. 包名 28 | 2. 版本 29 | 3. 架构(arch) 30 | 4. 依赖(Depends) 31 | 5. 推荐(Recommands) 32 | 6. 建议(Suggests) 33 | 7. 描述 34 | 35 | ## 已安装包的状态 36 | 37 | dpkg -s 38 | 39 | ## 查看包属性 40 | 41 | dpkg -I 42 | 43 | # dpkg内容 44 | 45 | ## 包内容 46 | 47 | 1. 所包含的文件,大小,属主,权限,日期 48 | 2. 安装前后脚本(preinst/postinst) 49 | 3. 卸载前后脚本(prerm/postrm) 50 | 4. 冲突文件(conffiles) 51 | 52 | ## 列出包里面包含的文件 53 | 54 | dpkg -L 55 | 56 | ## 查看包内容 57 | 58 | dpkg -c 59 | 60 | # dpkg配置 61 | 62 | ## debconf 63 | 64 | 用于配置包的交互系统。 65 | 66 | ## 包状态 67 | 68 | 1. Status: Not/Inst/Conf-files/Unpacked/halF-conf/Half-inst/trig-aWait/Trig-pend 69 | 2. Err: (none)/Reinst-required (Status,Err: uppercase=bad) 70 | 71 | 状态标明了包在系统中处于哪个阶段。 72 | 73 | ## 安装包 74 | 75 | dpkg -i 76 | 77 | ## 删除包 78 | 79 | dpkg -r 80 | 81 | 删除配置 82 | 83 | dpkg -P 84 | 85 | ## 配置继续 86 | 87 | dpkg --configure -a 88 | 89 | # dpkg杂项 90 | 91 | ## 查找某个文件属于哪个包 92 | 93 | dpkg -S filepath 94 | 95 | ## 不安装解开包内容 96 | 97 | dpkg -x 98 | 99 | # apt系统 100 | 101 | ## 简述 102 | 103 | 用于管理程序源的基础系统。 104 | 105 | ## 包列表 106 | 107 | 整个系统中所列的所有源里面载明的包列表。 108 | 109 | 列表里面有数据,指明某个包可以在哪个url取得,大小多少,md5/sha1/sha256。因此下载列表所指明的文件是不会遭到替换的(碰撞算法不算)。 110 | 111 | ## 更新包列表 112 | 113 | apt-get update 114 | aptitude update 115 | 116 | ## 下载包 117 | 118 | apt-get download 119 | aptitude download 120 | 121 | 下载源码 122 | 123 | apt-get source 124 | aptitude source 125 | 126 | ## 安装 127 | 128 | apt-get install 129 | aptitude install 130 | 131 | ## 删除 132 | 133 | apt-get remove 134 | aptitude remove 135 | 136 | 删除带配置 137 | 138 | apt-get purge 139 | aptitude purge 140 | 141 | # apt源 142 | 143 | ## 源配置 144 | 145 | /etc/apt/sources.list文件中,每行指明一个源。 146 | 147 | deb http://192.168.1.22:9999/debian/ wheezy main contrib non-free 148 | 149 | 1. 第一部分指明是二进制源(deb)还是源码源(deb-src)。 150 | 2. 第二部分指明基url。 151 | 3. 第三部分指明release。 152 | 4. 第四部分和后续指明category。 153 | 154 | ## 源的三要素 155 | 156 | 1. release,指发行版本。例如wheezy/stable, jessie/testing, sid/unstable,或者是precise/lucid等。 157 | 2. category,仓库分类。可自行选定的仓库分类,大部分发行上只有一个,即main。但是有补充,例如debian的non-free和ubuntu的restricted。 158 | 3. arch,cpu架构。无需指定安装时固定。可以用dpkg --print-architecture查看和管理。debian目前已经支持multiarch,但是效果不好说。 159 | 160 | ## 可信赖源 161 | 162 | 源给出的包列表使得下载包是安全的,但是包列表本身还可能遭到替换。为了解决这个问题,需要对包列表进行签名。apt使用的是gpg签名系统。在每个系统上,都安装了可信签名公钥。 163 | 164 | sudo apt-key list 165 | 166 | 凡是经过可信密钥签名的仓库,都是可信仓库。而非可信仓库添加进去的第一件事,就是添加签名公钥。 167 | 168 | ## 自建源缓存 169 | 170 | 安装approx,修改/etc/approx/approx.conf 171 | 172 | 每行一条,开始指明缓存路径,后续指明上游目标。例如: 173 | 174 | debian http://mirrors.ustc.edu.cn/debian 175 | ubuntu http://mirrors.163.com/ubuntu 176 | gplhost http://mirrors.shell909090.org/gplhost 177 | 178 | 第一条说明[http://server:9999/debian](http://server:9999/debian)映射到[http://mirrors.ustc.edu.cn/debian](http://mirrors.ustc.edu.cn/debian)。后续同。第三条为openstack在wheezy上的backport附加源。 179 | 180 | # 依赖求解 181 | 182 | ## 依赖关系图 183 | 184 | 1. Depends, Recommands。 185 | 2. 依赖版本>=和==。 186 | 187 | ## 手工安装和依赖安装 188 | 189 | aptitude search '.*' | grep '^i' 190 | apt-mark showmanual 191 | apt-mark showauto 192 | 193 | ## 冲突 194 | 195 | apt-cache show nginx-light 196 | Conflicts: nginx-extras, nginx-full, nginx-naxsi 197 | 198 | 冲突主要用于解决有相同路径的文件,具有替代功能的程序,或不兼容的版本。 199 | 200 | ## 版本升级 201 | 202 | apt-get upgrade 203 | aptitude upgrade 204 | 205 | 在版本升级中,很容易出现冲突。例如新包依赖于一个新的库,但是系统中某个未升级的程序依赖于一个老的库。两者都是强制依赖,因此不能同时存在于系统中。 206 | 207 | ## 冲突解决方案 208 | 209 | 使用aptitude,进入后可以看到冲突的存在。按E查看冲突解决方案,或调整方案。 210 | 211 | 最有效的方法是自行求解。通过自行标记某些包为安装或删除状态,解除冲突。 212 | 213 | ## 只升级安全补丁 214 | 215 | unattended-upgrades 216 | 217 | 由于升级的量更小,因此几乎不可能碰到冲突。 218 | -------------------------------------------------------------------------------- /md/docker.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | docker 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 |
17 |
18 |

Docker不是虚拟机

19 | 20 |
21 |
22 |
23 |
24 |
25 |

虚拟化的种类和层级

26 |
    27 |
  1. cpu虚拟化:可以模拟不同CPU,例如bochs
  2. 28 |
  3. 完全虚拟化:只能模拟同样CPU,但是可以执行不同系统,例如vmware
  4. 29 |
  5. 半虚拟化:guest必须打补丁,例如Xen
  6. 30 |
  7. 硬件虚拟化:可以当作获得硬件加速的完全虚拟化
  8. 31 |
  9. 系统虚拟化:host和guest共享一样的内核,例如Openvz
  10. 32 |
  11. 语言沙盒:只能在语言的范围内使用
  12. 33 |
34 | 35 |
36 |
37 | 38 |

虚拟化的级别越偏底层,速度越慢,用户越难察觉到虚拟化的存在。

39 |

虚拟化的级别越偏上层,速度越快,用户越容易感知。

40 |
    41 |
  1. cpu虚拟化和完全虚拟化时,用户几乎可以不察觉到虚拟化的存在
  2. 42 |
  3. 半虚拟化时,guest内核必须存在补丁
  4. 43 |
  5. 系统虚拟化时,用户不能控制自己的内核
  6. 44 |
  7. 语言沙盒时,用户没有使用api的自由
  8. 45 |
46 | 47 |
48 |
49 |
50 |
51 |
52 |

docker的实现结构

53 |
    54 |
  • docker 55 |
      56 |
    • lxc
    • 57 |
    • namespace: 仅沙盒隔离,不限制资源。
    • 58 |
    • cgroup: 仅限制资源,不沙盒隔离。
    • 59 |
    • aufs
    • 60 |
    • image管理
    • 61 |
  • 62 |
63 |

当然,还有很多细节的东西,里面就不一一列举了。例如veth。

64 | 65 |
66 |
67 |
68 |
69 |
70 |

docker不是虚拟机

71 |

docker不是虚拟机,因为lxc已经是虚拟机。

72 |

如果两者功能一样,那么docker就没有存在的必要。

73 |

你可以把docker当虚拟机用,但是当虚拟机用的话,他的完备程度远远不及现在的种种虚拟机。

74 |

相比之下,就会觉得很不好用。这不是docker的错,只能说被不正确的使用了。

75 | 76 |
77 |
78 |

docker是什么

79 |

docker就是环境。

80 |

docker实际上只做了一件事情——镜像管理。负责将可执行的镜像导入导出,在不同设备上迁移。

81 | 82 |
83 |
84 | 85 |

原本我们发布软件有两种方法,源码发布和二进制发布。二进制发布又有两种方案,静态链接和动态链接。

86 |

最早的时候,我们发布软件都喜欢动态链接,因为小。

87 |

但是随着网络和存储的升级,软件越来越喜欢静态链接,或者把动态库打包到发布里。

88 |

因为系统情况越来越复杂,依赖关系一旦出错,系统就无法启动。

89 |

将这个思路推到极限,就是虚拟机发布。早些年有人发过一些Oracle的linux安装镜像,算的上是先驱。

90 |

因为Oracle早些年的安装程序很难用,对系统的依赖复杂。公司做测试用装一套Oracle还不够麻烦的。

91 |

相比起来,下载一个虚拟机直接跑起来就可以用就方便了很多。即使性能差一些,测试而已也不是特别在意。

92 |

docker再进了一步。不但提供一个镜像,可以在系统间方便的迁移。而且连镜像的升级都能做掉。

93 |

更爽的是,升级只用传输差量数据。

94 |

当然,有好处就有牺牲。

95 | 96 |
97 |
98 |

docker的镜像是只读的

99 |

其实不是,docker的镜像当然可以写入。但是写的时候有几个问题。

100 |
    101 |
  1. 如果对镜像进行写入,aufs会将原始文件复制一次,再进行写入。这样性能比较低。
  2. 102 |
  3. 更直接的问题是,一旦对镜像做了写入,就无法从docker这里获得更新支持——docker不能将你的写入和上游的更新合并。因此,整个系统就退化成了一个完全的虚拟机。
  4. 103 |
104 |

所以,我个人认为,docker的镜像本身应当是只读的——如同EC2里面一样。数据的写入应当通过远程文件系统或者数据库服务来解决。

105 | 106 |
107 |
108 |
109 |
110 |
111 |
112 |

vagrant

113 |

提到镜像管理,我们可以提一下同样属于镜像管理的一个软件——vagrant。

114 |
    115 |
  1. 可以将vbox的镜像打包导入导出
  2. 116 |
  3. 提供了一个cloud,允许镜像的分享/更新
  4. 117 |
118 | 119 |
120 |
121 |

为什么vagrant不如docker出名

122 |
    123 |
  • 快,系统级虚拟化使得docker的虚拟化开销降低到百分级别以下。
  • 124 |
  • 可以在虚拟机内使用的虚拟机,例如云主机内。
  • 125 |
  • 资源调度灵活,不需要将资源预先划定给不同的实例,在不同资源的机器上也不用调整参数。
  • 126 |
127 | 128 |
129 |
130 |
131 |
132 |
133 |
134 |

典型用例

135 | 136 |
137 |
138 |

编译系统/打包系统/集成测试环境

139 |

典型的搭建一次,执行一次,销毁一次。不需要对image做更改(准确说的需要做更改,但是不需要保存)。

140 | 141 |
142 |
143 |

公司内部应用

144 |

在IaaS的比拼中,以Openvz为代表的系统化虚拟化方案几乎完败于完全虚拟化/半虚拟化系列技术。就我和朋友的讨论,这里面最主要的因素在于。完全虚拟化技术可以比较好的隔离实例和实例间的资源使用,而系统虚拟化技术更偏向于将资源充分利用。这使得系统虚拟化更容易超售。

145 |

然而,在公司内部应用中,这一缺陷就变成了优势。企业的诸多系统,只要在同一个优先级,其可用性应当是一致的。几个联动系统中,一个资源不足陷于濒死的情况下,保持其他几个系统资源充足并无意义。而且总资源是否足够应当是得到充分保证的事情,企业自己“超售”自己的资源,使得业务系统陷入运行缓慢的境地一点意义都没有。

146 | 147 |
148 |
149 | 150 |

因此,系统虚拟化可以为企业级云计算提供可以灵活调度的资源,和非常低的额外开销。

151 |

当然,云计算在企业化中原本就面临一些问题。原本提供软-硬件统一解决方案的集成商,需要如何重新组织解决方案。如何协调节约资源和高性能,高可用。云计算在企业级应用中还有很长的路要走。

152 | 153 |
154 |
155 |
156 |
157 |
158 |

短板

159 |
    160 |
  • 太新。目前成功案例还是不足,而且围绕docker的工具链还不完备。
  • 161 |
  • 适用范围比较窄。需求需要集中在“环境迁移”领域,而且image本身不应被写入。
  • 162 |
  • 生不逢时。rvm和virtualenv已经在前面了。
  • 163 |
164 | 165 |
166 |
167 |
168 |
169 |

FAQ

170 | 171 |
172 |
173 |
174 | 175 | 176 | 202 | 203 | 204 | -------------------------------------------------------------------------------- /md/docker.md: -------------------------------------------------------------------------------- 1 | # Docker不是虚拟机 2 | 3 | # 虚拟化的种类和层级 4 | 5 | 1. cpu虚拟化:可以模拟不同CPU,例如bochs 6 | 2. 完全虚拟化:只能模拟同样CPU,但是可以执行不同系统,例如vmware 7 | 3. 半虚拟化:guest必须打补丁,例如Xen 8 | 4. 硬件虚拟化:可以当作获得硬件加速的完全虚拟化 9 | 5. 系统虚拟化:host和guest共享一样的内核,例如Openvz 10 | 6. 语言沙盒:只能在语言的范围内使用 11 | 12 | --- 13 | 14 | 虚拟化的级别越偏底层,速度越慢,用户越难察觉到虚拟化的存在。 15 | 16 | 虚拟化的级别越偏上层,速度越快,用户越容易感知。 17 | 18 | 1. cpu虚拟化和完全虚拟化时,用户几乎可以不察觉到虚拟化的存在 19 | 2. 半虚拟化时,guest内核必须存在补丁 20 | 3. 系统虚拟化时,用户不能控制自己的内核 21 | 4. 语言沙盒时,用户没有使用api的自由 22 | 23 | # docker的实现结构 24 | 25 | * docker 26 | * lxc 27 | * namespace: 仅沙盒隔离,不限制资源。 28 | * cgroup: 仅限制资源,不沙盒隔离。 29 | * aufs 30 | * image管理 31 | 32 | 当然,还有很多细节的东西,里面就不一一列举了。例如veth。 33 | 34 | # docker不是虚拟机 35 | 36 | docker不是虚拟机,因为lxc已经是虚拟机。 37 | 38 | 如果两者功能一样,那么docker就没有存在的必要。 39 | 40 | 你可以把docker当虚拟机用,但是当虚拟机用的话,他的完备程度远远不及现在的种种虚拟机。 41 | 42 | 相比之下,就会觉得很不好用。这不是docker的错,只能说被不正确的使用了。 43 | 44 | ## docker是什么 45 | 46 | docker就是环境。 47 | 48 | docker实际上只做了一件事情——镜像管理。负责将可执行的镜像导入导出,在不同设备上迁移。 49 | 50 | --- 51 | 52 | 原本我们发布软件有两种方法,源码发布和二进制发布。二进制发布又有两种方案,静态链接和动态链接。 53 | 54 | 最早的时候,我们发布软件都喜欢动态链接,因为小。 55 | 56 | 但是随着网络和存储的升级,软件越来越喜欢静态链接,或者把动态库打包到发布里。 57 | 58 | 因为系统情况越来越复杂,依赖关系一旦出错,系统就无法启动。 59 | 60 | 将这个思路推到极限,就是虚拟机发布。早些年有人发过一些Oracle的linux安装镜像,算的上是先驱。 61 | 62 | 因为Oracle早些年的安装程序很难用,对系统的依赖复杂。公司做测试用装一套Oracle还不够麻烦的。 63 | 64 | 相比起来,下载一个虚拟机直接跑起来就可以用就方便了很多。即使性能差一些,测试而已也不是特别在意。 65 | 66 | docker再进了一步。不但提供一个镜像,可以在系统间方便的迁移。而且连镜像的升级都能做掉。 67 | 68 | 更爽的是,升级只用传输差量数据。 69 | 70 | 当然,有好处就有牺牲。 71 | 72 | ## docker的镜像是只读的 73 | 74 | 其实不是,docker的镜像当然可以写入。但是写的时候有几个问题。 75 | 76 | 1. 如果对镜像进行写入,aufs会将原始文件复制一次,再进行写入。这样性能比较低。 77 | 2. 更直接的问题是,一旦对镜像做了写入,就无法从docker这里获得更新支持——docker不能将你的写入和上游的更新合并。因此,整个系统就退化成了一个完全的虚拟机。 78 | 79 | 所以,我个人认为,docker的镜像本身应当是只读的——如同EC2里面一样。数据的写入应当通过远程文件系统或者数据库服务来解决。 80 | 81 | # vagrant 82 | 83 | 提到镜像管理,我们可以提一下同样属于镜像管理的一个软件——vagrant。 84 | 85 | 1. 可以将vbox的镜像打包导入导出 86 | 2. 提供了一个cloud,允许镜像的分享/更新 87 | 88 | ## 为什么vagrant不如docker出名 89 | 90 | * 快,系统级虚拟化使得docker的虚拟化开销降低到百分级别以下。 91 | * 可以在虚拟机内使用的虚拟机,例如云主机内。 92 | * 资源调度灵活,不需要将资源预先划定给不同的实例,在不同资源的机器上也不用调整参数。 93 | 94 | # 典型用例 95 | 96 | ## 编译系统/打包系统/集成测试环境 97 | 98 | 典型的搭建一次,执行一次,销毁一次。不需要对image做更改(准确说的需要做更改,但是不需要保存)。 99 | 100 | ## 公司内部应用 101 | 102 | 在IaaS的比拼中,以Openvz为代表的系统化虚拟化方案几乎完败于完全虚拟化/半虚拟化系列技术。就我和朋友的讨论,这里面最主要的因素在于。完全虚拟化技术可以比较好的隔离实例和实例间的资源使用,而系统虚拟化技术更偏向于将资源充分利用。这使得系统虚拟化更容易超售。 103 | 104 | 然而,在公司内部应用中,这一缺陷就变成了优势。企业的诸多系统,只要在同一个优先级,其可用性应当是一致的。几个联动系统中,一个资源不足陷于濒死的情况下,保持其他几个系统资源充足并无意义。而且总资源是否足够应当是得到充分保证的事情,企业自己“超售”自己的资源,使得业务系统陷入运行缓慢的境地一点意义都没有。 105 | 106 | --- 107 | 108 | 因此,系统虚拟化可以为企业级云计算提供可以灵活调度的资源,和非常低的额外开销。 109 | 110 | 当然,云计算在企业化中原本就面临一些问题。原本提供软-硬件统一解决方案的集成商,需要如何重新组织解决方案。如何协调节约资源和高性能,高可用。云计算在企业级应用中还有很长的路要走。 111 | 112 | # 短板 113 | 114 | * 太新。目前成功案例还是不足,而且围绕docker的工具链还不完备。 115 | * 适用范围比较窄。需求需要集中在“环境迁移”领域,而且image本身不应被写入。 116 | * 生不逢时。rvm和virtualenv已经在前面了。 117 | 118 | # FAQ 119 | -------------------------------------------------------------------------------- /md/docker1.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | docker1 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 |
17 |
18 |
19 |

原理

20 | 21 |
22 |
23 |

chroot

24 |
    25 |
  • 最原始的隔离容器
  • 26 |
  • 只改变进程的文件系统(从而使得进程的环境发生变化),不改变其他
  • 27 |
  • 可以看到其他进程,打开网络,发送消息
  • 28 |
  • 经常配合用户权限控制系统做jail
  • 29 |
  • 但是在jail内的应用能够了解自己并没有root权限(例如无法新建用户)
  • 30 |
31 | 32 |
33 |
34 |

namespace

35 |
    36 |
  • 多种对象隔离,例如IPC,网络
  • 37 |
  • 一个进程只能属于一个ns,一个ns可以拥有多个进程
  • 38 |
  • 进程的子进程默认属于同一个ns
  • 39 |
  • 有些东西不在隔离范围内
  • 40 |
41 |

如果将用户隔离在ns内,删除CAP_SYS_ADMIN,删除对主机设备的访问,删除mount能力(以及其他适当权限)。即使是root用户也无法访问主机。因此,namespace配合chroot可以完美的切换用户环境。用户感知自己是root,可以做root权限的很多行为,例如添加删除用户。只有和系统有关的行为无法进行,例如加载内核模块。

42 | 43 |
44 |
45 | 46 |

隔离种类

47 |
    48 |
  • uts: 简单来说等价于hostname
  • 49 |
  • ipc: 共享内存,信号两,消息队列等
  • 50 |
  • pid: 进程编号
  • 51 |
  • mount: 可挂载的文件系统
  • 52 |
  • user: 用户/uid映射系统
  • 53 |
  • network: 网络设备隔离
  • 54 |
55 | 56 |
57 |
58 |

cgroup

59 |

虽然namespace可以隔离用户,但是却不能隔离用户的资源使用量。

60 |

cgroup主要是将环境所需资源尽量限制在一个范围内。

61 |

主要功能:

62 |
    63 |
  • 限制资源使用:例如内存使用上限,CPU亲缘性
  • 64 |
  • 控制优先级:例如CPU优先级
  • 65 |
  • 记账:例如内存水平和CPU使用情况
  • 66 |
  • 控制:挂起和继续
  • 67 |
68 | 69 |
70 |
71 | 72 |

无法控制:

73 |
    74 |
  • 目前为止,最大CPU用量无法控制,只能控制CPU亲缘性。
  • 75 |
  • 就我测试的结果,内存用量和ps统计的值略有偏差。
  • 76 |
77 | 78 |
79 |
80 |

lxc

81 |

结合namespace和cgroup做的虚拟机,和openvz类似。

82 |

docker在linux下使用lxc作为其底层,在windows下据说可以使用其他底层。

83 | 84 |
85 |
86 |

aufs

87 |

aufs是一个多层的混合文件系统(Union file system)。

88 |
    89 |
  • 多层的文件系统栈,可写的在最上
  • 90 |
  • 当读取文件时,从上到下查找
  • 91 |
  • 当写入文件时,写入最上层的可写系统。
  • 92 |
  • 如果可写系统内不存在原始文件,首先复制。
  • 93 |
94 |

因此写入的数据会覆盖原始数据,形成一个可读写文件系统的假象。

95 |

经常被用来给只读设备加上临时的可写特性,例如Live CDs。

96 | 97 |
98 |
99 | 100 |

特性:

101 |
    102 |
  • 可以指定多个可读文件系统(有顺序)
  • 103 |
  • 可以指定一个可写文件系统
  • 104 |
  • 最高可达127层
  • 105 |
  • 原生文件级的读写效率
  • 106 |
  • 在文件查找时和复制原始文件到可写系统时有开销
  • 107 |
108 | 109 |
110 |
111 |

docker

112 |

docker是基于lxc和aufs实现的容器系统,其主要目标是专注于将环境带到不同的机器上。

113 |

换句话说,如果不是和环境有关的问题,并不需要使用docker。

114 | 115 |
116 |
117 |
118 |
119 |
120 |
121 |

概念

122 |
    123 |
  • 镜像: 一个可运行的环境(例如文件系统),但尚未有任何程序载入内存。
  • 124 |
  • 容器: 载入内存的镜像,其中包含一个或多个镜像(层叠)。
  • 125 |
  • 仓库: 某个网络上的位置,通常是docker hub,也可以指定一个自己的服务器(register server)。
  • 126 |
  • tag: 标签,用来标记仓库中的某个特定版本。
  • 127 |
128 | 129 |
130 |
131 |

工作模式

132 |

每个镜像是一个文件系统,可以依赖于另一个文件系统。在实际使用的时候,将镜像和其依赖链组成aufs链条。因此新的镜像内容会覆盖老的(如果存在同名)。

133 |

将镜像内的任何一个程序载入内存运行,即成为容器。一个容器内至少需要包含一个进程,可以包含多个。

134 |

将镜像(和其依赖的链条)上传到某个文件服务器,该文件服务器即是仓库。一个仓库可以包含多个镜像,一般使用镜像的hash值的一部分作为镜像的ID。

135 |

为了简化仓库存取,为每个ID取的别名。

136 | 137 |
138 |
139 |
140 |
141 |
142 |
143 |

用法

144 | 145 |
146 |
147 |

启动和终止容器

148 |

用以下指令启动一个容器。

149 |
docker run [-i] [-d] [仓库]:[tag] cmds
150 | 
151 |
    152 |
  • -i: 交互模式。一般配合-t使用。
  • 153 |
  • -d: daemon模式。在后台启动程序并且detach。
  • 154 |
155 |

PS. 如果镜像不存在,会从远程下载。

156 | 157 |
158 |
159 | 160 |

用以下指令终止容器。

161 |
docker stop [容器id]
162 | 
163 |

用以下指令重新启动一个终止了的容器。

164 |
docker start [容器id]
165 | 
166 |

以下指令停止一个容器再重新启动。

167 |
docker restart [容器id]
168 | 
169 | 170 |
171 |
172 |

查看容器

173 |
docker ps [-a]
174 | 
175 |
    176 |
  • -a: 查看所有容器,包括已经终止的。
  • 177 |
178 | 179 |
180 |
181 |

删除

182 |
docker rm
183 | 
184 |

PS. 容器必须已经终止,否则需要用-f强制终止后删除。

185 | 186 |
187 |
188 |

关联到容器

189 |
docker attach
190 | 
191 | 192 |
193 |
194 |

获得镜像

195 |
docker pull [仓库]:[tag]
196 | 
197 | 198 |
199 |
200 |

列出镜像

201 |
docker images
202 | 
203 | 204 |
205 |
206 |

创建镜像

207 |

可以从现有镜像,启动为一个容器进行修改,最后再提交。

208 |
docker commit [容器ID] [仓库]:[tag]
209 | 
210 | 211 |
212 |
213 | 214 |

也可以准备一个dockerfile,自动的从现有镜像修改提交。

215 |
docker build
216 | 
217 | 218 |
219 |
220 | 221 |

也可以从本地文件导入。

222 |
cat files.tar.gz | docker import - [仓库]:[tag]
223 | 
224 | 225 |
226 |
227 |

保存和载入

228 |
docker save -o filename.tar [仓库]:[tag]
229 | 
230 | docker load --input filename.tar
231 | 
232 | 233 |
234 |
235 |

删除镜像

236 |
docker rmi [仓库]:[tag]
237 | 
238 | 239 |
240 |
241 |

上传镜像

242 |
docker push
243 | 
244 | 245 |
246 |
247 |

修改镜像标签

248 |
docker tag
249 | 
250 | 251 |
252 |
253 |
254 |
255 |
256 |
257 |

优劣

258 |

重要的不是docker自身有什么优劣,而是和虚拟化相比有什么优劣。

259 | 260 |
261 |
262 |

优势

263 |
    264 |
  • 高效的执行效率
  • 265 |
  • 灵活的主机内调度(其实就是隔离不完备)
  • 266 |
  • 差量传输更新(只需要更新依赖链上没有下载的部分)
  • 267 |
268 | 269 |
270 |
271 |

劣势

272 |
    273 |
  • 没有成熟的管理工具
  • 274 |
  • 案例不够多
  • 275 |
  • 没有分布式存储系统
  • 276 |
  • 更弱的隔离
  • 277 |
278 | 279 |
280 |
281 |
282 |
283 |
284 |
285 |

场景

286 | 287 |
288 |
289 |

方案发布

290 |
    291 |
  • 大型系统复杂的依赖
  • 292 |
  • docker发布一键安装
  • 293 |
  • 差量传输更新,方便升级
  • 294 |
  • 易于部署集群
  • 295 |
  • 易于最大利用集群资源
  • 296 |
  • 由于所有应用都是为同一个使用者服务,因此不存在单个应用“压死”其他应用的问题
  • 297 |
298 | 299 |
300 |
301 |

PaaS

302 |

用于做paas的通用中间层,解决调试和标准的问题。

303 |
    304 |
  • 在本地用docker调试和打包,远程可以在PaaS云上执行
  • 305 |
  • 由于环境标准化,用户不再担心被“锁定”在一家PaaS上
  • 306 |
307 | 308 |
309 |
310 | 311 |

缺点:

312 |
    313 |
  • 由于缺乏隔离,因此在单台机器上的应用会影响到同机应用,为了解决性能问题,引发频繁迁移
  • 314 |
  • 同样由于缺乏隔离,因此无法限定最大使用量,只能限定请求数量
  • 315 |
316 | 317 |
318 |
319 |
320 |
321 |
322 |
323 |

展望

324 |
    325 |
  • docker并不完美,目前还缺少很多东西
  • 326 |
  • 一般不建议在企业环境中直接使用docker,而是将其作为工具的底层使用
  • 327 |
  • 即使目前并不使用,你也应当关注docker。因为将来他很可能改变和你有关的技术生态
  • 328 |
329 | 330 |
331 |
332 |

演进

333 |
    334 |
  • 增加分布式文件系统支持
  • 335 |
  • 增加灵活的网络支持(SDN)
  • 336 |
  • 官方的管理工具
  • 337 |
338 | 339 |
340 |
341 |

合作

342 |
    343 |
  • 基于docker的云平台系统
  • 344 |
  • 基于docker的发行版
  • 345 |
  • 基于docker的系统
  • 346 |
347 | 348 |
349 |
350 |
351 | 363 |
364 |
365 |

Q&A

366 | 367 |
368 |
369 |
370 | 371 | 372 | 398 | 399 | 400 | -------------------------------------------------------------------------------- /md/docker1.md: -------------------------------------------------------------------------------- 1 | # 原理 2 | 3 | ## chroot 4 | 5 | * 最原始的隔离容器 6 | * 只改变进程的文件系统(从而使得进程的环境发生变化),不改变其他 7 | * 可以看到其他进程,打开网络,发送消息 8 | * 经常配合用户权限控制系统做jail 9 | * 但是在jail内的应用能够了解自己并没有root权限(例如无法新建用户) 10 | 11 | ## namespace 12 | 13 | * 多种对象隔离,例如IPC,网络 14 | * 一个进程只能属于一个ns,一个ns可以拥有多个进程 15 | * 进程的子进程默认属于同一个ns 16 | * 有些东西不在隔离范围内 17 | 18 | 如果将用户隔离在ns内,删除CAP\_SYS\_ADMIN,删除对主机设备的访问,删除mount能力(以及其他适当权限)。即使是root用户也无法访问主机。因此,namespace配合chroot可以完美的切换用户环境。用户感知自己是root,可以做root权限的很多行为,例如添加删除用户。只有和系统有关的行为无法进行,例如加载内核模块。 19 | 20 | --- 21 | 22 | 隔离种类 23 | 24 | * uts: 简单来说等价于hostname 25 | * ipc: 共享内存,信号两,消息队列等 26 | * pid: 进程编号 27 | * mount: 可挂载的文件系统 28 | * user: 用户/uid映射系统 29 | * network: 网络设备隔离 30 | 31 | ## cgroup 32 | 33 | 虽然namespace可以隔离用户,但是却不能隔离用户的资源使用量。 34 | 35 | cgroup主要是将环境所需资源尽量限制在一个范围内。 36 | 37 | 主要功能: 38 | 39 | * 限制资源使用:例如内存使用上限,CPU亲缘性 40 | * 控制优先级:例如CPU优先级 41 | * 记账:例如内存水平和CPU使用情况 42 | * 控制:挂起和继续 43 | 44 | --- 45 | 46 | 无法控制: 47 | 48 | * 目前为止,最大CPU用量无法控制,只能控制CPU亲缘性。 49 | * 就我测试的结果,内存用量和ps统计的值略有偏差。 50 | 51 | ## lxc 52 | 53 | 结合namespace和cgroup做的虚拟机,和openvz类似。 54 | 55 | docker在linux下使用lxc作为其底层,在windows下据说可以使用其他底层。 56 | 57 | ## aufs 58 | 59 | aufs是一个多层的混合文件系统(Union file system)。 60 | 61 | * 多层的文件系统栈,可写的在最上 62 | * 当读取文件时,从上到下查找 63 | * 当写入文件时,写入最上层的可写系统。 64 | * 如果可写系统内不存在原始文件,首先复制。 65 | 66 | 因此写入的数据会覆盖原始数据,形成一个可读写文件系统的假象。 67 | 68 | 经常被用来给只读设备加上临时的可写特性,例如Live CDs。 69 | 70 | --- 71 | 72 | 特性: 73 | 74 | * 可以指定多个可读文件系统(有顺序) 75 | * 可以指定一个可写文件系统 76 | * 最高可达127层 77 | * 原生文件级的读写效率 78 | * 在文件查找时和复制原始文件到可写系统时有开销 79 | 80 | ## docker 81 | 82 | docker是基于lxc和aufs实现的容器系统,其主要目标是专注于将环境带到不同的机器上。 83 | 84 | 换句话说,如果不是和环境有关的问题,并不需要使用docker。 85 | 86 | # 概念 87 | 88 | * 镜像: 一个可运行的环境(例如文件系统),但尚未有任何程序载入内存。 89 | * 容器: 载入内存的镜像,其中包含一个或多个镜像(层叠)。 90 | * 仓库: 某个网络上的位置,通常是docker hub,也可以指定一个自己的服务器(register server)。 91 | * tag: 标签,用来标记仓库中的某个特定版本。 92 | 93 | ## 工作模式 94 | 95 | 每个镜像是一个文件系统,可以依赖于另一个文件系统。在实际使用的时候,将镜像和其依赖链组成aufs链条。因此新的镜像内容会覆盖老的(如果存在同名)。 96 | 97 | 将镜像内的任何一个程序载入内存运行,即成为容器。一个容器内至少需要包含一个进程,可以包含多个。 98 | 99 | 将镜像(和其依赖的链条)上传到某个文件服务器,该文件服务器即是仓库。一个仓库可以包含多个镜像,一般使用镜像的hash值的一部分作为镜像的ID。 100 | 101 | 为了简化仓库存取,为每个ID取的别名。 102 | 103 | # 用法 104 | 105 | ## 启动和终止容器 106 | 107 | 用以下指令启动一个容器。 108 | 109 | docker run [-i] [-d] [仓库]:[tag] cmds 110 | 111 | * -i: 交互模式。一般配合-t使用。 112 | * -d: daemon模式。在后台启动程序并且detach。 113 | 114 | PS. 如果镜像不存在,会从远程下载。 115 | 116 | --- 117 | 118 | 用以下指令终止容器。 119 | 120 | docker stop [容器id] 121 | 122 | 用以下指令重新启动一个终止了的容器。 123 | 124 | docker start [容器id] 125 | 126 | 以下指令停止一个容器再重新启动。 127 | 128 | docker restart [容器id] 129 | 130 | ## 查看容器 131 | 132 | docker ps [-a] 133 | 134 | * -a: 查看所有容器,包括已经终止的。 135 | 136 | ## 删除 137 | 138 | docker rm 139 | 140 | PS. 容器必须已经终止,否则需要用-f强制终止后删除。 141 | 142 | ## 关联到容器 143 | 144 | docker attach 145 | 146 | ## 获得镜像 147 | 148 | docker pull [仓库]:[tag] 149 | 150 | ## 列出镜像 151 | 152 | docker images 153 | 154 | ## 创建镜像 155 | 156 | 可以从现有镜像,启动为一个容器进行修改,最后再提交。 157 | 158 | docker commit [容器ID] [仓库]:[tag] 159 | 160 | --- 161 | 162 | 也可以准备一个dockerfile,自动的从现有镜像修改提交。 163 | 164 | docker build 165 | 166 | --- 167 | 168 | 也可以从本地文件导入。 169 | 170 | cat files.tar.gz | docker import - [仓库]:[tag] 171 | 172 | ## 保存和载入 173 | 174 | docker save -o filename.tar [仓库]:[tag] 175 | 176 | docker load --input filename.tar 177 | 178 | ## 删除镜像 179 | 180 | docker rmi [仓库]:[tag] 181 | 182 | ## 上传镜像 183 | 184 | docker push 185 | 186 | ## 修改镜像标签 187 | 188 | docker tag 189 | 190 | # 优劣 191 | 192 | 重要的不是docker自身有什么优劣,而是和虚拟化相比有什么优劣。 193 | 194 | ## 优势 195 | 196 | * 高效的执行效率 197 | * 灵活的主机内调度(其实就是隔离不完备) 198 | * 差量传输更新(只需要更新依赖链上没有下载的部分) 199 | 200 | ## 劣势 201 | 202 | * 没有成熟的管理工具 203 | * 案例不够多 204 | * 没有分布式存储系统 205 | * 更弱的隔离 206 | 207 | # 场景 208 | 209 | ## 方案发布 210 | 211 | * 大型系统复杂的依赖 212 | * docker发布一键安装 213 | * 差量传输更新,方便升级 214 | * 易于部署集群 215 | * 易于最大利用集群资源 216 | * 由于所有应用都是为同一个使用者服务,因此不存在单个应用“压死”其他应用的问题 217 | 218 | ## PaaS 219 | 220 | 用于做paas的通用中间层,解决调试和标准的问题。 221 | 222 | * 在本地用docker调试和打包,远程可以在PaaS云上执行 223 | * 由于环境标准化,用户不再担心被“锁定”在一家PaaS上 224 | 225 | --- 226 | 227 | 缺点: 228 | 229 | * 由于缺乏隔离,因此在单台机器上的应用会影响到同机应用,为了解决性能问题,引发频繁迁移 230 | * 同样由于缺乏隔离,因此无法限定最大使用量,只能限定请求数量 231 | 232 | # 展望 233 | 234 | * docker并不完美,目前还缺少很多东西 235 | * 一般不建议在企业环境中直接使用docker,而是将其作为工具的底层使用 236 | * 即使目前并不使用,你也应当关注docker。因为将来他很可能改变和你有关的技术生态 237 | 238 | ## 演进 239 | 240 | * 增加分布式文件系统支持 241 | * 增加灵活的网络支持(SDN) 242 | * 官方的管理工具 243 | 244 | ## 合作 245 | 246 | * 基于docker的云平台系统 247 | * 基于docker的发行版 248 | * 基于docker的系统 249 | 250 | # 引用 251 | 252 | * [Docker —— 从入门到实践](http://dockerpool.com/static/books/docker_practice/index.html) 253 | * [Docker基础技术:Linux CGroup](http://coolshell.cn/articles/17049.html) 254 | * [Docker基础技术:Linux Namespace(上)](http://coolshell.cn/articles/17010.html) 255 | * [Docker基础技术:Linux Namespace(下)](http://coolshell.cn/articles/17029.html) 256 | 257 | # Q&A 258 | -------------------------------------------------------------------------------- /md/kernel-read.html: -------------------------------------------------------------------------------- 1 |

Abstract

2 | 3 |
    4 |
  1. Quick introduce how TCP handshake works, how listen and accept works.
  2. 5 |
  3. Introduce several ways and tools to read kernel.
  4. 6 |
  5. Demonstrate how to do it.
  6. 7 |
8 | 9 |

Keywords

10 | 11 |
    12 |
  • linux
  • 13 |
  • tcp
  • 14 |
  • listen
  • 15 |
  • accept
  • 16 |
  • synflood
  • 17 |
18 | 19 |

Copyright & License

20 | 21 |

Copyright (C) 2019 Shell.Xu shell909090@gmail.com

22 | 23 |

Text is available under the Creative Commons Attribution-ShareAlike License

24 | 25 |

摘要

26 | 27 |
    28 |
  1. 简单介绍tcp握手原理,理解listen和accept的机制。
  2. 29 |
  3. 介绍代码阅读的几种手段和工具。
  4. 30 |
  5. 简单演示如何阅读linux源码。
  6. 31 |
32 | 33 |

TCP握手

34 | 35 |

TCP握手可以简单分为三步[13],但实际上加上前后过程总共有六步。

36 | 37 |

假定发起方为A,接收方为B。A的连接socket称为Ac。B的listen socket称为Bl,accept出来的socket成为Ba。

38 | 39 |
    40 |
  1. B首先建立Bl,调用listen,设定队列大小。 (前置步骤例如建立socket等省略)
  2. 41 |
  3. A对Ac调用connect,内核发送SYN报文。
  4. 42 |
  5. B内核收到SYN报文,找到Bl。Bl在内核经过一定处理后决定是否返回SYN&ACK报文。
  6. 43 |
  7. A收到SYN&ACK报文,找到Ac。Ac在内核经过一定处理后返回ACK报文,并且A连接建立就绪,可以收发数据。
  8. 44 |
  9. B收到ACK报文,找到或建立Ba。Ba连接建立就绪,进入accept队列。
  10. 45 |
  11. B对Bl调用accept,获得Ba。
  12. 46 |
47 | 48 |

不同系统实现的差别有:

49 | 50 |
    51 |
  1. 第3步中,内核如何决定是否返回SYN&ACK报文。其中主要考虑SYN flood[3][4]的防御。
  2. 52 |
  3. Ba在第3步建立还是第5步建立。
  4. 53 |
  5. Ba在第3步到第5步的状态(我们称为半握手状态),是否放入accept队列,还是保持一个单独队列[1]。
  6. 54 |
  7. 如何加速上述流程。
  8. 55 |
56 | 57 |

manual

58 | 59 |

根据文献[10]

60 | 61 |
The behavior of the backlog argument on TCP sockets changed with Linux 2.2.  Now it specifies the queue length for completely established sockets waiting to be accepted, instead of the number  of  incomplete  connection  re‐
 62 | quests.   The  maximum  length  of  the  queue for incomplete sockets can be set using /proc/sys/net/ipv4/tcp_max_syn_backlog.  When syncookies are enabled there is no logical maximum length and this setting is ignored.  See
 63 | tcp(7) for more information.
 64 | 
65 | 66 |

utils

67 | 68 |
    69 |
  1. lxr
  2. 70 |
  3. github repository
  4. 71 |
  5. github release
  6. 72 |
73 | 74 |

阅读重点

75 | 76 |
    77 |
  1. struct sock
  2. 78 |
  3. struct socket
  4. 79 |
  5. struct inet_connection_sock
  6. 80 |
  7. struct inet_sock
  8. 81 |
82 | 83 |

sock和socket互相指向。inet_connection_sock包含inet_sock,inet_sock包含sock。

84 | 85 |
    86 |
  1. EXPORT_SYMBOL(inet_listen)
  2. 87 |
  3. EXPORT_SYMBOL(inet_accept)
  4. 88 |
  5. tcp_v4_rcv 89 |
      90 |
    1. __inet_lookup_skb 91 |
        92 |
      1. __inet_lookup
      2. 93 |
      3. __inet_lookup_established 开链法(Separate chaining with linked lists)[8][9]
      4. 94 |
      5. __inet_lookup_listener 注意reuseport[11][12]和INADDR_ANY的处理
      6. 95 |
    2. 96 |
    3. tcp_check_req 97 |
        98 |
      1. inet_csk_complete_hashdance
      2. 99 |
      3. inet_csk_reqsk_queue_add
      4. 100 |
    4. 101 |
    5. tcp_v4_do_rcv 102 |
        103 |
      1. tcp_rcv_state_process 重点process
      2. 104 |
      3. tcp_v4_conn_request
      4. 105 |
      5. tcp_conn_request
      6. 106 |
      7. inet_csk_reqsk_queue_hash_add req加hash表,不是sk
      8. 107 |
      9. reqsk_queue_hash_req
      10. 108 |
      11. inet_ehash_insert
      12. 109 |
    6. 110 |
  6. 111 |
112 | 113 |

结论

114 | 115 |
    116 |
  1. 当没有synflood[3][4]时,正常返回。synflood时(超过sysctl_max_syn_backlog[2]的3/4),未开启syncookie[4][5]则丢弃。
  2. 117 |
  3. Ba在第3步建立,而后进入hashinfo结构。第5步通过hashinfo找到Ba,检验ack通过后进入accept_queue。
  4. 118 |
  5. Ba在半握手状态直接装入hashinfo。后续使用定时器重发SYN&ACK,直到超过tcp_synack_retries[2]规定的极限。
  6. 119 |
  7. fastopen[6][7]有助于这个过程。
  8. 120 |
121 | 122 |

references

123 | 124 |
    125 |
  1. How TCP backlog works in Linux [14]
  2. 126 |
  3. ip-sysctl.txt
  4. 127 |
  5. SYN flood
  6. 128 |
  7. SYN Flood Mitigation with synsanity [15]
  8. 129 |
  9. SYN cookies
  10. 130 |
  11. TCP Fast Open
  12. 131 |
  13. TCP-Fast-Open-Experimentation
  14. 132 |
  15. Hash table
  16. 133 |
  17. An hashtable implementation in C
  18. 134 |
  19. listen
  20. 135 |
  21. socket
  22. 136 |
  23. The SO_REUSEPORT socket option
  24. 137 |
  25. Transmission Control Protocol#Connection establishment
  26. 138 |
  27. github bsd kernel
  28. 139 |
  29. tproxy
  30. 140 |
141 | -------------------------------------------------------------------------------- /md/kernel-read.md: -------------------------------------------------------------------------------- 1 | # Abstract 2 | 3 | 1. Quick introduce how TCP handshake works, how listen and accept works. 4 | 2. Introduce several ways and tools to read kernel. 5 | 3. Demonstrate how to do it. 6 | 7 | # Keywords 8 | 9 | * linux 10 | * tcp 11 | * listen 12 | * accept 13 | * synflood 14 | 15 | # Copyright & License 16 | 17 | Copyright (C) 2019 Shell.Xu 18 | 19 | Text is available under the [Creative Commons Attribution-ShareAlike License](https://creativecommons.org/licenses/by-sa/4.0/legalcode) 20 | 21 | # 摘要 22 | 23 | 1. 简单介绍tcp握手原理,理解listen和accept的机制。 24 | 2. 介绍代码阅读的几种手段和工具。 25 | 3. 简单演示如何阅读linux源码。 26 | 27 | # TCP握手 28 | 29 | TCP握手可以简单分为三步[13],但实际上加上前后过程总共有六步。 30 | 31 | 假定发起方为A,接收方为B。A的连接socket称为Ac。B的listen socket称为Bl,accept出来的socket成为Ba。 32 | 33 | 1. B首先建立Bl,调用listen,设定队列大小。 (前置步骤例如建立socket等省略) 34 | 2. A对Ac调用connect,内核发送SYN报文。 35 | 3. B内核收到SYN报文,找到Bl。Bl在内核经过一定处理后决定是否返回SYN&ACK报文。 36 | 4. A收到SYN&ACK报文,找到Ac。Ac在内核经过一定处理后返回ACK报文,并且A连接建立就绪,可以收发数据。 37 | 5. B收到ACK报文,找到或建立Ba。Ba连接建立就绪,进入accept队列。 38 | 6. B对Bl调用accept,获得Ba。 39 | 40 | 不同系统实现的差别有: 41 | 42 | 1. 第3步中,内核如何决定是否返回SYN&ACK报文。其中主要考虑SYN flood[3][4]的防御。 43 | 2. Ba在第3步建立还是第5步建立。 44 | 3. Ba在第3步到第5步的状态(我们称为半握手状态),是否放入accept队列,还是保持一个单独队列[1]。 45 | 4. 如何加速上述流程。 46 | 47 | # manual 48 | 49 | 根据文献[10] 50 | 51 | The behavior of the backlog argument on TCP sockets changed with Linux 2.2. Now it specifies the queue length for completely established sockets waiting to be accepted, instead of the number of incomplete connection re‐ 52 | quests. The maximum length of the queue for incomplete sockets can be set using /proc/sys/net/ipv4/tcp_max_syn_backlog. When syncookies are enabled there is no logical maximum length and this setting is ignored. See 53 | tcp(7) for more information. 54 | 55 | # utils 56 | 57 | 1. [lxr](https://elixir.bootlin.com/linux/latest/source) 58 | 2. [github repository](https://github.com/torvalds/linux) 59 | 3. [github release](https://github.com/torvalds/linux/releases) 60 | 61 | # 阅读重点 62 | 63 | 1. struct sock 64 | 2. struct socket 65 | 2. struct inet\_connection\_sock 66 | 3. struct inet\_sock 67 | 68 | sock和socket互相指向。inet\_connection\_sock包含inet\_sock,inet\_sock包含sock。 69 | 70 | 1. EXPORT\_SYMBOL(inet\_listen) 71 | 2. EXPORT\_SYMBOL(inet\_accept) 72 | 3. tcp\_v4\_rcv 73 | 1. \_\_inet\_lookup\_skb 74 | 1. \_\_inet\_lookup 75 | 2. \_\_inet\_lookup\_established 开链法(Separate chaining with linked lists)[8][9] 76 | 3. \_\_inet\_lookup\_listener 注意reuseport[11][12]和INADDR_ANY的处理 77 | 2. tcp\_check\_req 78 | 1. inet\_csk\_complete\_hashdance 79 | 2. inet\_csk\_reqsk\_queue\_add 80 | 3. tcp\_v4\_do\_rcv 81 | 1. tcp\_rcv\_state\_process 重点process 82 | 2. tcp\_v4\_conn\_request 83 | 3. tcp\_conn\_request 84 | 4. inet\_csk\_reqsk\_queue\_hash\_add req加hash表,不是sk 85 | 5. reqsk\_queue\_hash\_req 86 | 6. inet\_ehash\_insert 87 | 88 | # 结论 89 | 90 | 1. 当没有synflood[3][4]时,正常返回。synflood时(超过sysctl\_max\_syn\_backlog[2]的3/4),未开启syncookie[4][5]则丢弃。 91 | 2. Ba在第3步建立,而后进入hashinfo结构。第5步通过hashinfo找到Ba,检验ack通过后进入accept\_queue。 92 | 3. Ba在半握手状态直接装入hashinfo。后续使用定时器重发SYN&ACK,直到超过tcp\_synack\_retries[2]规定的极限。 93 | 4. fastopen[6][7]有助于这个过程。 94 | 95 | # references 96 | 97 | 1. [How TCP backlog works in Linux](http://veithen.io/2014/01/01/how-tcp-backlog-works-in-linux.html) [14] 98 | 2. [ip-sysctl.txt](https://github.com/torvalds/linux/blob/master/Documentation/networking/ip-sysctl.txt) 99 | 3. [SYN flood](https://en.wikipedia.org/wiki/SYN_flood) 100 | 4. [SYN Flood Mitigation with synsanity](https://github.blog/2016-07-12-syn-flood-mitigation-with-synsanity/) [15] 101 | 5. [SYN cookies](https://en.wikipedia.org/wiki/SYN_cookies) 102 | 6. [TCP Fast Open](https://en.wikipedia.org/wiki/TCP_Fast_Open) 103 | 7. [TCP-Fast-Open-Experimentation](https://github.com/derikclive/TCP-Fast-Open-Experimentation) 104 | 8. [Hash table](https://en.wikipedia.org/wiki/Hash_table) 105 | 9. [An hashtable implementation in C](https://gist.github.com/phsym/4605704) 106 | 10. [listen](http://man7.org/linux/man-pages/man2/listen.2.html) 107 | 11. [socket](http://man7.org/linux/man-pages/man7/socket.7.html) 108 | 12. [The SO_REUSEPORT socket option](https://lwn.net/Articles/542629/) 109 | 13. [Transmission Control Protocol#Connection establishment](https://en.wikipedia.org/wiki/Transmission_Control_Protocol#Connection_establishment) 110 | 14. [github bsd kernel](https://github.com/freebsd/freebsd/blob/master/sys/netinet/tcp_input.c#L544) 111 | 15. [tproxy](https://github.com/torvalds/linux/blob/master/Documentation/networking/tproxy.txt) 112 | -------------------------------------------------------------------------------- /md/lic.md: -------------------------------------------------------------------------------- 1 | # 版权和专利 2 | 3 | * 著作权是正确称呼 4 | * 著作权保护实现,专利保护理念 5 | * 其实还有商标,外观等等 6 | 7 | ## 著作权划分 8 | 9 | * 精神权利 10 | * 署名权 11 | * 保持原作品的完整 12 | * 著作财产权 13 | * 重制权 14 | * 公开口述,播送,上映,演出,传输,展示权 15 | * 改作权 16 | * 散布权 17 | * 出租权 18 | * ... 19 | 20 | # 授权 21 | 22 | ## 样例 23 | 24 | DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE 25 | Version 2, December 2004 26 | 27 | Copyright (C) 2004 Sam Hocevar 28 | 29 | Everyone is permitted to copy and distribute verbatim or modified 30 | copies of this license document, and changing it is allowed as long 31 | as the name is changed. 32 | 33 | DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE 34 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 35 | 36 | 0. You just DO WHAT THE FUCK YOU WANT TO. 37 | 38 | (声明:以上文字仅是示例,不代表作者真实意图) 39 | 40 | ## 结合本文 41 | 42 | 如果使用了WTFPL会发生什么事 43 | 44 | --- 45 | 46 | 但是我仍然没有放弃著作权。 47 | 48 | ## 什么叫做放弃著作权 49 | 50 | 这个slide不是我写的。 51 | 52 | (声明:以上内容仅仅是举例,也不代表作者真实意愿) 53 | 54 | ## LICENSE 55 | 56 | 本slide的真正授权: 57 | 58 | [cc-by-sa3.0](http://creativecommons.org/licenses/by-sa/3.0/deed.zh) 59 | 60 | # 我为什么要共享 61 | 62 | * 个人的良好意愿 63 | * 一个人的力量不足以做出足够好的作品 64 | * 多人合作作品,又不愿意协调共同所有权的问题 65 | * 限于时间/精力,不愿意再维护程序,但是又不甘心放弃自己的工作 66 | 67 | # 可不可以不要追究这些细节 68 | 69 | 可以,只要你能接受。。。 70 | 71 | ## 一个血淋淋的案例 72 | 73 | wikipedia的[XviD](http://zh.wikipedia.org/wiki/Xvid)页面上说: 74 | 75 | 在以往,个人电脑只能使用微软开发的MPEG-4 Codec,即MS MPEG4 V1、MS MPEG4 V2、MS MPEG4 V3的系列编码核心。其中 76 | 以MS MPEG4 V3的画质最好[来源请求]。不过其只可用在ASF文件,但这个文件格式是封闭的。故此网上有些黑客将其改写 77 | 为著名的MPEG4 Codec DivX 3.11。 78 | 79 | 但问题是,DivX 3.11的基础技术是非法盗用微软的,无法进行更广泛的产品化及生产硬件播放机。因此,一些精通视频编 80 | 码的工程师(包括原DivX 3.11的开发者)成立了一家名为DivX Networks Inc.的公司,简称DXN。他们主导了完全符合ISO 81 | MPEG4标准的Open DivX CODEC的开发,并吸引了许多软件高手参与。这时,主要的编程工作是由DXN来做,但很多技术上的 82 | 难题却是由开放源代码社区帮忙解决。 83 | 84 | 但因为整个计划并不是根据GPL开发的,而是LGPL,因此在Open DivX即将成形时,DXN借此漏洞将其闭源,结果使众多开放 85 | 源代码社区的义工感到被出卖。也是因为这个原因,整个0day组织永远的拒绝了DXN公司的DivX格式。 86 | 87 | 而原Open DivX计划的义工最后决定在最后一个Open DivX版本的基础上,编写XviD(将DivX反过来写)以继续原Open DivX 88 | 的目的。 89 | 90 | 大约1年后,Xvid计划的开发者重写了所有代码,并依照GPL发布(而不再是LGPL,所以谁要是想用它做成产品而不开放源代 91 | 码是非法的)。但因为某些国家如美国,日本有软件专利法,使得其在该地区可能出现法律纠纷。因此,Xvid官方网站只提 92 | 供源代码下载,用户只可由第三方网站下载第三方的安装文件。 93 | 94 | ## 漏洞的原因 95 | 96 | LGPL允许其他程序链接到这些代码而本身不需要开源。 97 | 98 | 想象一下有两个代码仓库,A和B。A是LGPL的,B是商业授权。两者合起来能够完成完整的编码工作。 99 | 100 | 平时有很多社区的义工在A仓库中为B实现功能。但事实上离开B,A是不能独立运作的。 101 | 102 | DXN做的事,就是把B封闭源码,并借助版权法保证即使义工接触到了源码,也不能使用。 103 | 104 | 也许有人会说,那做一个不同的实现,补上B的代码就好了。这就是XviD的工作。 105 | 106 | 在比较大的软件工程中,不会有人随时随地检查协议。更不会有人对协议如此敏感。 107 | 108 | 但是在这次事件之后,整个欧美开源社区对LGPL都非常警惕。当有合作时,会要求彼此一定要采用GPL而非LGPL。 109 | 110 | # 自由软体的基本权力 111 | 112 | * 自由使用 113 | * 自由研究/修改 114 | * 自由散布 115 | * 自由改善并重发布 116 | 117 | --- 118 | 119 | 免费软体(Freeware)和自由软件(Free Software)是不一样的。 120 | 121 | 开放源代码(Open Source)不等于自由软件(Free Software)。 122 | 123 | ## 软件,为演进而生 124 | 125 | 为什么改善和重发布的自由那么重要。 126 | 127 | 因为软件最重要的一个特性就是不停的演进。当用户有更新的需求的时候,他们可以自行改善。 128 | 129 | 并且这一改善可以通过上游优化和吸收,再发布。使得成千上万的最终用户获益。 130 | 131 | 同样,当软件的作者不再愿意维护软件,甚至不再存活时,其他用户也可以接手这一工作,使得软件继续生存。 132 | 133 | 相反,如果是闭源软件,当作者出现问题时,其后续就很难说。 134 | 135 | ## 大教堂和市集 136 | 137 | ESR在1997年提出的软件工程学思想。将软件开发模式分为两种。 138 | 139 | * 大教堂模式:有一个专属的团队进行开发和演进。 140 | * 市集模式:没有一个专属的团队进行开发。每个用户开发自己需要的功能,上游进行合并。用户也可以产生自己的分支,并负责维护。 141 | 142 | 理论上看,市集模式的开发和调试速度要明显快过大教堂模式,因为可以在上面堆积的人力更加多一些。 143 | 144 | 实际上也是如此,采取大教堂模式的wikipedia,其词条数目已经远远超过了大英百科全书。 145 | 146 | ## 后软件时代的诗 147 | 148 | 市集模式,实际上是后软件时代的诗。他出现的前提,是有很多用户,有能力改进源码,翻译文档,或者至少可以提出修改意见。 149 | 150 | 而不是像软件初生的那个年代一般,只有专业的程序员,才能编写程序给普通人使用。 151 | 152 | 即使大部分用户并不具备修改源码的能力,能够看到源码并交由其他人修改就是一项非常重要的权力了。 153 | 154 | ## 保证软件的可维护性 155 | 156 | 如果你必须需要某个软件中增加一项功能,怎么办? 157 | 158 | 如果是闭源软件,没有任何办法。你唯一的办法就是联系原作者,看看他是否愿意为了你开发一个定制版本。 159 | 160 | 价格贵不说,更有可能因为原作者没有时间而无计可施。 161 | 162 | 开源软件至少保证你如下权利:你有权下载一份源码,然后找一家有能力开发的公司,和他们签署一个合同。要求在这个源码之上添加一项功能。 163 | 164 | 当然,很多协议同时要求,如果你需要向其他人提供这个修改后的版本,必须同时提供修改后的源码。 165 | 166 | # 自由软件商业化方案 167 | 168 | * 开源授权和闭源授权 169 | * 免费软件和收费服务 170 | 171 | # 授权是作者意愿的表达 172 | 173 | 作者希望用户能做什么,限制用户不能做什么,都在授权协议中做了统一规范。因此可以说授权是作者意愿的表达。 174 | 175 | 尊重授权,才能更好的鼓励作者和协作者,协调他们的关系。 176 | 177 | 我们可以想想,如果不尊重作者意愿,作者是否还愿意开发好的软件,并持续维护呢?这是一个很难说的问题。 178 | 179 | 固然很多作者不会因为几个不尊重作者的用户而终止开发。他们最多就是将这些用户钉上耻辱柱(ffmpeg耻辱柱)。 180 | 181 | 但是一定有作者,因为得不到用户的尊重而停止开发。 182 | 183 | # 开源协议分类 184 | 185 | ## copyleft 186 | 187 | copyleft是一个非常重要的理念。他指的是,你是否希望基于你的代码衍生的代码必须使用相容的授权。 188 | 189 | 大致来说,上面的cc-sa就是此类。 190 | 191 | 如果你坚持copyleft,那么应当使用cc-sa类协议或者GPL2/3。前者常用于文件/文档/艺术品,而后者常用于软件。 192 | 193 | ## 其他人修改源码后,是否可以闭源 194 | 195 | 如果你选择了copyleft,当然不用考虑这个问题了——他自然必须使用开源协议发布。 196 | 197 | 如果你不介意其他人修改源码后闭源,那么应当采用BSD,MIT,Apache授权。 198 | 199 | 三者的基本差异在于,MIT允许使用你的名字促销,而BSD不允许。Apache则要求每个修改过的文件,都必须放置版权说明。 200 | 201 | ## 其他人修改后不能闭源,但是不必copyleft 202 | 203 | 这是一个很奇怪的选择。可选的项目有LGPL和Mozilla许可。 204 | 205 | ## 有点绕 206 | 207 | 请看[这张图](http://image.beekka.com/blog/201105/free_software_licenses.png)。 208 | 209 | ![](http://image.beekka.com/blog/201105/free_software_licenses.png) 210 | 211 | ## 简单来说 212 | 213 | 如果你希望衍生者也加入开源的行列,应当使用copyleft协议,例如cc-sa和GPL。 214 | 215 | 如果你不介意衍生者的使用方式,只希望保留基本的署名权等权力。可以选用BSD和MIT——对于大部分人来说,两者的差异可以忽略。 216 | 217 | # 开源协议代表 218 | 219 | ## GPL 220 | 221 | GPL最广为人知的特性就是他的“传染”性。关于这点,我请教过一位比较专业的同行。 222 | 223 | 他的说法是,在C类语言中,如果要使用GPL类库,就必须包含原始头文件。而这一过程就构成了“包含和重构”。 224 | 225 | 因此链接方式调用的库就很难避免被传染GPL。而调用方式则不需要引用任何GPL许可的文件,因此可以规避。 226 | 227 | 因此我特别问了他。如果是python类的脚本代码,在使用对方库的时候,不需要包含引用任何代码(duck typing),是否可以规避GPL。 228 | 229 | 他的意见是——不知道。 230 | 231 | ## 如何实施 232 | 233 | * 去[这个页面](http://www.gnu.org/licenses/gpl-2.0.html) 234 | * 找到“How to Apply These Terms to Your New Programs”一节 235 | * 把上面那段实例增加到LICENSE文件里面,把版权人和年份改掉 236 | * 如果程序是交互的,需要一个单独的功能“显示授权”,如同下面的示例那样 237 | * 如果这是你的职务发明,你需要让雇主签署一个“放弃版权”声明,以避免自己的问题 238 | 239 | ## BSD 240 | 241 | BSD几乎不禁止用户的任何行为。但是在衍生产品的源码和二进制产品中,必须包含BSD的声明,而且不可以用原作者的名字来推广。 242 | 243 | ## 如何实施(用户) 244 | 245 | * 在文档中指明程序中包含了来自XX项目的内容,这部分内容满足以下授权,并且将BSD的版权文字放在下面 246 | * 在二进制发行中也需要包含这份文档 247 | * 在任何广告中,需要包含“本软件使用了XX的XX项目”的声明(仅对4-条款必须) 248 | * 不用作者的名字做广告(不得以作者名字背书或者促销)(3,4-条款版本必须) 249 | * 2-条款版本基本和MIT是一样的 250 | 251 | ## 如何实施(作者) 252 | 253 | * 去[这个页面](http://opensource.org/licenses/BSD-3-Clause) 254 | * 找合适的授权文字,2-Clause/3-Clause 255 | * 复制内容到你项目的LICENSE里面,把版权人和年份改掉 256 | 257 | ## CC 258 | 259 | CC(Creative Commons)是一个知名的非盈利组织。CC系列协议是一系列创作共用协议的统称。下属N种子协议: 260 | 261 | * by: 署名。您(使用者)可以复制、发行、展览、表演、放映、广播或通过信息网络传播本作品;您必须按照作者或者许可人指定的方式对作品进行署名。 262 | * nc: 非商业性使用。 263 | * nd: 禁止修改作品。 264 | * sa: 相同方式。若您改变、转变或更改本作品,仅在遵守与本作品相同的授权条款下,您才能散布由本作品产生的衍生作品。 265 | 266 | --- 267 | 268 | 例如,这个slide的授权叫做cc-by-sa3.0,即cc3.0版本协议中,需要署名和相同方式使用的。在这个协议下: 269 | 270 | * 您可以自由复制、散布、展示,演出以及衍生本作品 271 | * 您必须适当的提及本作品(如果可以,应当提及作者和组织,并提供版权声明,授权声明和弃权声明。4.0协议要求在许可的情况下,提及标题) 272 | * 您如果基于这个作品衍生,衍生作品必须兼容cc-by-sa3.0协议 273 | 274 | ## 如何实施 275 | 276 | * 声明自己的版权权力。 277 | * 你需要给出材料基于CC某个子协议的声明,并附带一个指向具体CC协议的链接。 278 | 279 | # 我们为什么要注重法律的繁文褥节 280 | 281 | 作为作者来说,请参考上面“血淋淋的例子”。 282 | 283 | 作为用户来说,请对善意的开放源码,简化了你的工作的人。保持最基本的尊重。 284 | 285 | # 开源不是拿来主义 286 | 287 | 中国(不仅仅是大陆)这里,很多人对开源的认识还停留在“啊,这个东西不错,还有源码,我可以拿来用用”的地步。 288 | 289 | 实际上,拿来主义不是开源。 290 | 291 | 开源并不排斥拿来主义。实际上,开源就是要你们拿去用的。但是在拿去之外,他还提出了一些最低限度的要求。 292 | 293 | 例如要在二进制发行里面加入原作者和授权声明,或者引用源码的代码必须开源。原则上,这些东西都是必须遵守的,而非可选项。 294 | 295 | 可能有很多人对这些可选项不屑一顾,因为作者不可能为了不存在的损失而打一场不会赢的官司。 296 | 297 | 这是中国开源界的可悲现状,对此没什么办法。 298 | 299 | --- 300 | 301 | 但是至少,我们可以做一些可以做的努力。例如按照规范注明原作者和授权声明。 302 | 303 | 如果对利益无伤的话,尽量将我们的修改推到上游去做合并。 304 | 305 | 实际上这些工作很可能对我们反而有益——例如将修改推入上游可以避免我们自行维护一套分支。 306 | 307 | 当上游集合了其他人修改的新版本推出,并且上面包含我们所需要的新功能时,我们不需要做合并升级。 308 | 309 | # 从哪里开始 310 | 311 | * 给自己的blog加上cc-by-sa3.0协议声明 312 | * 引用别人的内容/源码时,看一眼自己是不是可以做到授权 313 | * 帮助社区编写/翻译文档。并且保证原文档满足协议(参考上文),保证自己的文档带有协议 314 | * 使用不满足授权的软件/内容时,感到羞愧 315 | -------------------------------------------------------------------------------- /md/lic_tip.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | lic_tip 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 |
17 |
18 |

版权和专利

19 |
    20 |
  • 灰姑娘是著作权,描写出身高贵的男性和出身贫寒的女性的爱情故事,获得读者的喜爱,这一想法是专利(如果申请的下来的话)
  • 21 |
  • 在澳大利亚的判决中,Victoria Park Racing and Recreation Grounds Co. Ltd v. Taylor (1937) 58 CLR 479 at 498,Latham 大法官用一个人从巴士上跌落为例,第一个报道该事件的人不能用著作权法来防止其他人对该事件进行的相同报道。
  • 22 |
23 | 24 |
25 |
26 |
27 |
28 |

著作权划分

29 |
    30 |
  • 《保护文学和艺术作品伯尔尼公约》:关于著作权保护的国际条约
  • 31 |
  • 中国的规定是永久,美国的规定是允许作者协议放弃
  • 32 |
  • 假名和拒绝署名是允许的
  • 33 |
  • 禁止随意演绎
  • 34 |
  • 禁止歪曲的仅是原意。如果基于其上衍生,属于财产权。
  • 35 |
  • 日本的同人作品,如果没有原作者许可,是违法的么? 36 |
      37 |
    • 如果歪曲原作剧情,并假托作者名义(金圣叹中枪)应是违法。
    • 38 |
    • 如果仅仅借助人设,重新构架剧情,不好说
    • 39 |
  • 40 |
41 | 42 |
43 |
44 |
45 |
46 |

结合本文

47 |

在不获得授权的情况下:

48 |
    49 |
  • 转载/重新发布
  • 50 |
  • 在其他地方演讲/并收费
  • 51 |
  • 更改演讲标题/内容
  • 52 |
  • 更改署名(在中国不可以,这点不确定)
  • 53 |
  • 但是可以借助署名打广告
  • 54 |
  • 直接卖讲稿收费
  • 55 |
  • 把演讲内容录像放到网上播出/并收费
  • 56 |
57 | 58 |
59 |
60 |
61 |
62 |

我为什么要共享

63 |
    64 |
  • cc的创始人Lessig,在2004年写了一本书《自由文化》。cc授权。在几天内,就出现了中文版和给盲人的录音版本。
  • 65 |
  • 本文大量的引用了wikipedia的资料,和其他cc资料。如果没有这些基础工作,理论上我需要获得每个作者的同意,否则只能合理引用部分内容。
  • 66 |
  • 这篇slide,也许有人会基于其上做一些快速演讲,或者其他什么东西。我认为这些都可以,收费也可以。不需要获得我的许可。
  • 67 |
  • 如果真的有人这么做了,我希望你通知我一下。我很高兴我的内容能够帮助你。
  • 68 |
69 | 70 |
71 |
72 |
73 |
74 |

一个血淋淋的案例

75 |

实际上,在华蟒社区也有类似问题。当然,华蟒的问题是协议所无法解决的。

76 | 77 |
78 |
79 |
80 |
81 |

自由软体的几项基本权利

82 |

免费软件通常只给予使用的权力,并且这一权力还会受到各种但书的影响。例如给作者邮寄明信片啦。这些有时被称为共享软件(shareware)。

83 |

这些软件不一定开放源码,因此没有研究/修改的自由。或者有条件开放源码(例如微软的不可泄漏协议)。

84 |

即使开放源码,如果用户没有改善并重新发布的自由,那么也不能称之为自由软件。

85 | 86 |
87 |
88 |
89 |
90 |
91 |

软件,为演进而生

92 |
    93 |
  • RMS移交了emacs的管理权限
  • 94 |
  • mcomix继承了comix的代码
  • 95 |
  • reiserfs作者(Hans Reiser)入狱并没有阻碍系统演进(虽然其功能发展大幅受到抑制)
  • 96 |
  • MariaDB继承了Mysql的大部分功能
  • 97 |
98 | 99 |
100 |
101 | 102 |

作为对比的:

103 |
    104 |
  • PKZIP作者挂了
  • 105 |
  • 微软在买入foxpro后,为了自家的sqlserver,雪藏了这一软件,使其基本死亡
  • 106 |
107 | 108 |
109 |
110 |
111 |
112 |
113 |

大教堂和市集

114 |

ESR(Eric Steven Raymond)这本书是采用cc授权写的。虽然是纸质实体销售,但是如果将其放在网络上散布是合法的。因此网络上有诸多文字翻译版本,这些也全是合法的。

115 | 116 |
117 |
118 |
119 |
120 |
121 |

自由软件商业化方案

122 |

一套模式,两种授权。通过对同一套代码,实行开源授权和闭源授权而收费。

123 |
    124 |
  • Ghostscript: AGPL和商业授权
  • 125 |
  • 洋葱头,cc-by-nc-nd,禁止修改,禁止商用。因此需要商用和衍生需要问原作者独立购买。
  • 126 |
  • 对同一套内容,作者可以以两套授权发布。
  • 127 |
  • 如果所引用的代码都没有问题的话(没有copyleft)
  • 128 |
  • 对后续版本,可以重新闭源(只要作者能够凑齐所需要的所有条件——特别是引用没有copyleft,以及所有版权所有者同意),但是已经开源的版本不得追朔,其衍生产品也不受控制。
  • 129 |
  • 例如我的blog虽然是cc-by-sa3.0,但是在新闻网站上转载的时候是不能兼容此项协议的。因此我从中去除了cc-sa相关内容,并进行了少量重写。重新以商业方式授权,收取稿费。
  • 130 |
131 | 132 |
133 |
134 | 135 |

免费软件和收费服务。

136 |
    137 |
  • 例如RH的订阅就是收费的。
  • 138 |
  • 基于wordpress的定制也是收费的。如果找wp作者做修改收的更贵。
  • 139 |
  • 基于开源软件组建的整体解决方案/服务,都是收费的。
  • 140 |
  • 开源不仅收用户的钱,甚至还可以收第三方的钱。例如某个第三方做了个硬件,找社区做了一套驱动。
  • 141 |
142 | 143 |
144 |
145 |
146 |
147 |
148 |

copyleft

149 |
    150 |
  • 冷知识:cc-by-sa3.0是兼容GFDL(GNU)的。而且是cc-by-sa3.0可衍生到GFDL,反向不可以。
  • 151 |
152 | 153 |
154 |
155 |
156 |
157 |

其他人修改源码后,是否可以闭源

158 |

很多重要的基础库必须能够被商业软件引用,因此只能采用此类授权。更准确的说法是如果不是此类授权,商业软件就要重复的制造轮子了。

159 | 160 |
161 |
162 |
163 |
164 |

有点绕

165 |

这张图也是cc-by-sa3.0的。

166 | 167 |
168 |
169 |
170 |
171 |

GPL

172 |
    173 |
  • linux特别声明,对API进行调用属于调用方式使用。对此种方式的法律风险作出了澄清。
  • 174 |
  • GPL2/3相比,主要有几个区别。 175 |
      176 |
    • Tivo-ization:限制了制造者通过硬件限制修改软件的权力。即使用GPL软件的设备制造商,必须允许用户执行他们自己修改的版本。
    • 177 |
    • 国际化:更多考虑其他国家而不仅是美国的法律
    • 178 |
    • 专利:特别处理了专利。软件的分发者自动为使用者提供了软件内含的专利的许可。
    • 179 |
    • 相容性:和其他开源协议的相容性
    • 180 |
  • 181 |
  • linux kernel仍然是GPL2
  • 182 |
183 | 184 |
185 |
186 |
187 |
188 |

开源不是拿来主义

189 |

中国的侵权官司,是以造成的经济后果来界定的。如果是违反开源协议,即使好不容易打上官司,也会因为“没有造成损失”而无法做任何事情。因为自由软件本身就(大多数情况下)不产生任何收入。即使打赢,也很难排除侵权方的使用,更不提产生天价赔偿。

190 | 191 |
192 |
193 |
194 |
195 |

从哪里开始

196 |
    197 |
  • 反正大多数时候,你不可能真的禁止别人转载
  • 198 |
  • 例如转载别人内容时,如果有cc-by。那么只要保留署名,就完全不用担心。原则上说,cc-by没有要求保留原始链接。
  • 199 |
  • 如果基于别人内容衍生,例如翻译,修改,再创作。当有cc-sa时,需要注意自己的创作是否满足cc-sa。
  • 200 |
  • 写代码时用到了BSD/MIT的库,是否在自己的代码里面放置README或者LICENSE来说明这点。其实这非常容易,并不困难。
  • 201 |
202 | 203 |
204 |
205 |
206 | 207 | 208 | 234 | 235 | 236 | -------------------------------------------------------------------------------- /md/lic_tip.md: -------------------------------------------------------------------------------- 1 | # 版权和专利 2 | 3 | * 灰姑娘是著作权,描写出身高贵的男性和出身贫寒的女性的爱情故事,获得读者的喜爱,这一想法是专利(如果申请的下来的话) 4 | * 在澳大利亚的判决中,Victoria Park Racing and Recreation Grounds Co. Ltd v. Taylor (1937) 58 CLR 479 at 498,Latham 大法官用一个人从巴士上跌落为例,第一个报道该事件的人不能用著作权法来防止其他人对该事件进行的相同报道。 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 | * cc的创始人Lessig,在2004年写了一本书《自由文化》。cc授权。在几天内,就出现了中文版和给盲人的录音版本。 32 | * 本文大量的引用了wikipedia的资料,和其他cc资料。如果没有这些基础工作,理论上我需要获得每个作者的同意,否则只能合理引用部分内容。 33 | * 这篇slide,也许有人会基于其上做一些快速演讲,或者其他什么东西。我认为这些都可以,收费也可以。不需要获得我的许可。 34 | * 如果真的有人这么做了,我希望你通知我一下。我很高兴我的内容能够帮助你。 35 | 36 | # 一个血淋淋的案例 37 | 38 | 实际上,在华蟒社区也有类似问题。当然,华蟒的问题是协议所无法解决的。 39 | 40 | # 自由软体的几项基本权利 41 | 42 | 免费软件通常只给予使用的权力,并且这一权力还会受到各种但书的影响。例如给作者邮寄明信片啦。这些有时被称为共享软件(shareware)。 43 | 44 | 这些软件不一定开放源码,因此没有研究/修改的自由。或者有条件开放源码(例如微软的不可泄漏协议)。 45 | 46 | 即使开放源码,如果用户没有改善并重新发布的自由,那么也不能称之为自由软件。 47 | 48 | # 软件,为演进而生 49 | 50 | * RMS移交了emacs的管理权限 51 | * mcomix继承了comix的代码 52 | * reiserfs作者(Hans Reiser)入狱并没有阻碍系统演进(虽然其功能发展大幅受到抑制) 53 | * MariaDB继承了Mysql的大部分功能 54 | 55 | --- 56 | 57 | 作为对比的: 58 | 59 | * PKZIP作者挂了 60 | * 微软在买入foxpro后,为了自家的sqlserver,雪藏了这一软件,使其基本死亡 61 | 62 | # 大教堂和市集 63 | 64 | ESR(Eric Steven Raymond)这本书是采用cc授权写的。虽然是纸质实体销售,但是如果将其放在网络上散布是合法的。因此网络上有诸多文字翻译版本,这些也全是合法的。 65 | 66 | # 自由软件商业化方案 67 | 68 | 一套模式,两种授权。通过对同一套代码,实行开源授权和闭源授权而收费。 69 | 70 | * Ghostscript: AGPL和商业授权 71 | * [洋葱头](http://zh.wikipedia.org/wiki/%E6%B4%8B%E8%94%A5%E9%A0%AD_(%E5%8F%B0%E7%81%A3%E6%BC%AB%E7%95%AB),cc-by-nc-nd,禁止修改,禁止商用。因此需要商用和衍生需要问原作者独立购买。 72 | * 对同一套内容,作者可以以两套授权发布。 73 | * 如果所引用的代码都没有问题的话(没有copyleft) 74 | * 对后续版本,可以重新闭源(只要作者能够凑齐所需要的所有条件——特别是引用没有copyleft,以及所有版权所有者同意),但是已经开源的版本不得追朔,其衍生产品也不受控制。 75 | * 例如我的blog虽然是cc-by-sa3.0,但是在新闻网站上转载的时候是不能兼容此项协议的。因此我从中去除了cc-sa相关内容,并进行了少量重写。重新以商业方式授权,收取稿费。 76 | 77 | --- 78 | 79 | 免费软件和收费服务。 80 | 81 | * 例如RH的订阅就是收费的。 82 | * 基于wordpress的定制也是收费的。如果找wp作者做修改收的更贵。 83 | * 基于开源软件组建的整体解决方案/服务,都是收费的。 84 | * 开源不仅收用户的钱,甚至还可以收第三方的钱。例如某个第三方做了个硬件,找社区做了一套驱动。 85 | 86 | # copyleft 87 | 88 | * 冷知识:cc-by-sa3.0是兼容GFDL(GNU)的。而且是cc-by-sa3.0可衍生到GFDL,反向不可以。 89 | 90 | # 其他人修改源码后,是否可以闭源 91 | 92 | 很多重要的基础库必须能够被商业软件引用,因此只能采用此类授权。更准确的说法是如果不是此类授权,商业软件就要重复的制造轮子了。 93 | 94 | # 有点绕 95 | 96 | 这张图也是cc-by-sa3.0的。 97 | 98 | # GPL 99 | 100 | * linux特别声明,对API进行调用属于调用方式使用。对此种方式的法律风险作出了澄清。 101 | * GPL2/3相比,主要有几个区别。 102 | * Tivo-ization:限制了制造者通过硬件限制修改软件的权力。即使用GPL软件的设备制造商,必须允许用户执行他们自己修改的版本。 103 | * 国际化:更多考虑其他国家而不仅是美国的法律 104 | * 专利:特别处理了专利。软件的分发者自动为使用者提供了软件内含的专利的许可。 105 | * 相容性:和其他开源协议的相容性 106 | * linux kernel仍然是GPL2 107 | 108 | # 开源不是拿来主义 109 | 110 | 中国的侵权官司,是以造成的经济后果来界定的。如果是违反开源协议,即使好不容易打上官司,也会因为“没有造成损失”而无法做任何事情。因为自由软件本身就(大多数情况下)不产生任何收入。即使打赢,也很难排除侵权方的使用,更不提产生天价赔偿。 111 | 112 | # 从哪里开始 113 | 114 | * 反正大多数时候,你不可能真的禁止别人转载 115 | * 例如转载别人内容时,如果有cc-by。那么只要保留署名,就完全不用担心。原则上说,cc-by没有要求保留原始链接。 116 | * 如果基于别人内容衍生,例如翻译,修改,再创作。当有cc-sa时,需要注意自己的创作是否满足cc-sa。 117 | * 写代码时用到了BSD/MIT的库,是否在自己的代码里面放置README或者LICENSE来说明这点。其实这非常容易,并不困难。 118 | -------------------------------------------------------------------------------- /md/linuxsec_basic.md: -------------------------------------------------------------------------------- 1 | %title: linux system security and tuning - basic 2 | %author: Shell.Xu 3 | %date: 2015-09-18 4 | 5 | 6 | 7 | -> linux system security and tuning - basic <- 8 | 9 | ------------------------------------------------- 10 | 11 | -> 目录 <- 12 | 13 | * linux基本系统设定 14 | * 设定sudo 15 | * 系统密码设定 16 | * 系统用户管理 17 | * 设定启动项 18 | * 设定apt 19 | * 防火墙设定 20 | * 设定iptables 21 | * ssh设定 22 | * 设定ssh服务端 23 | * ssh身份验证方法 24 | * 设定~/.ssh 25 | * 设定ssh client config 26 | * ssh的一些用法 27 | * 内核参数设定 28 | * 设定sysctl 29 | 30 | ------------------------------------------------- 31 | 32 | -> 设定sudo <- 33 | 34 | 安装sudo 35 | ^ 36 | adduser shell sudo 37 | ^ 38 | (options)%sudo ALL=(ALL:ALL) NOPASSWD: ALL 39 | ^ 40 | 完成后可以使用user身份工作,而不是root。 41 | 42 | ------------------------------------------------- 43 | 44 | -> 系统密码设定 <- 45 | 46 | 如果你的root密码强度小于10字节,那么加强强度。 47 | root密码在防护很多猜解上都有意义,加强root密码是不论原因的。 48 | 建议root密码半年到一年一换。 49 | ^ 50 | 51 | 系统密码主要有几个用途。 52 | 53 | 1. 防护console界面有人登录(例如有人获得虚拟机操作界面,或者接入了物理设备)。 54 | 2. 防止su。 55 | 3. 具有任意sudo权限的用户,被知道密码等同于root密码泄漏(su成该用户,sudo输密码)。 56 | ^ 57 | 58 | 所以从上面可以看出。 59 | 60 | 1. root不能禁用。 61 | 2. 具备sudo权限的用户,也需要设定一个强密码。 62 | 63 | ------------------------------------------------- 64 | 65 | -> 系统用户管理 <- 66 | 67 | passwd的第二个字段是可选的加密后密码,现已废弃。如果不带':x:'的用户肯定是无法正常使用的 68 | grep -v ':x:' /etc/passwd 69 | ^ 70 | 71 | 找到没有密码的用户 72 | cat /etc/shadow | cut -d: -f 1,2 | grep '!' 73 | ^ 74 | 75 | 找到被锁定的用户 76 | cat /etc/shadow | cut -d: -f 1,2 | grep '*' 77 | ^ 78 | 79 | passwd -u -l可以锁定解锁用户 80 | ^ 81 | 82 | 找到带有过期密码的用户 83 | cat /etc/shadow | cut -d: -f 1,2 | grep '!!' 84 | 85 | ------------------------------------------------- 86 | 87 | -> 设定启动项 <- 88 | 89 | 安装sysv-rc-conf。 90 | 或者查看/etc/rc2.d和/etc/rcS.d。 91 | 92 | ------------------------------------------------- 93 | 94 | -> 设定apt <- 95 | 96 | 检查/etc/apt/sources.list 97 | ^ 98 | 99 | 正常的apt设定,一般包括一个主mirror,和一个security mirror。 100 | 101 | 例如我本地的配置中,主mirror为http://mirrors.ustc.edu.cn/debian 102 | security mirror为http://security.debian.org/ 103 | 104 | 不是每个mirror都做了security的! 105 | ^ 106 | 107 | 在安装系统的最后一步,要设定合适的apt mirrors。更新列表并升级。 108 | 109 | sudo apt-get update 110 | sudo apt-get upgrade -s | grep -i security 111 | 112 | ------------------------------------------------- 113 | 114 | 需要分开的理由是。主mirror的镜像量很大,同步节点很多的时候,会有同步延迟周期。 115 | 从包进入主镜像,到你可以下载,中间有数小时到数天的延迟。 116 | 117 | 而安全有关的包不能停留数天后才安装。 118 | 因此security有关的mirrors越上游越好,内容越少越好。 119 | 120 | 一般我会直接做一个最上游的镜像,然后所有内部都指向镜像。 121 | 在万不得已时,可以清理缓存,直接从最上游更新补丁。 122 | 123 | 我也建议在允许的情况下,security不要使用缓存,自己能够控制的除外。 124 | 125 | ------------------------------------------------- 126 | 127 | -> 设定iptables <- 128 | 129 | 1. 安装iptables-persistent 130 | ^ 131 | 2. -A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT 132 | ^ 133 | 3. -A INPUT -i lo -j ACCEPT 134 | ^ 135 | 4. -A INPUT -p icmp -j ACCEPT 136 | ^ 137 | 5. -P INPUT DROP 138 | ^ 139 | 6. iptables-save > /etc/iptables/rules.v4 140 | ^ 141 | 7. 检查ip addr,如果有ipv6地址,需要同样设定ipv6。 142 | 143 | ------------------------------------------------- 144 | 145 | -> 设定ssh服务端 <- 146 | 147 | UseDNS no 148 | 关闭DNS可以减少登录时开销 149 | ^ 150 | 151 | PermitRootLogin no 152 | ^ 153 | 154 | PasswordAuthentication no 155 | 如果没有特别的理由,一定要禁用密码登录 156 | ^ 157 | 158 | 安装fail2ban(或者denyhosts) 159 | ^ 160 | 161 | 设定AllowUsers(options) 162 | ^ 163 | 164 | 设定ClientAliveInterval(options) 165 | ^ 166 | 167 | LogLevel to VERBOSE(options) 168 | 169 | ------------------------------------------------- 170 | 171 | -> ssh身份验证方法 <- 172 | 173 | pubkey是靠谱的方法,这应是第一建议和默认值 174 | ^ 175 | 176 | 启用密码登录的话,需要注意哪些用户可以登录。 177 | 在没有设定PermitEmptyPasswords和AllowUsers的情况下,会有潜在风险。 178 | 当然,PermitEmptyPasswords默认关闭。 179 | ^ 180 | 181 | 同时,有些很少用的帐号设定了弱密码,也一样会有问题。 182 | 所以最好禁止密码登录。一定要用的话建议开AllowUsers。 183 | ^ 184 | 185 | 禁用root登录在很多地方有额外好处,例如密码登录的情况下,可以防护猜解root密码。 186 | 虽然在关闭了密码登录,或者启用fail2ban的情况下不需要顾虑这个。 187 | ^ 188 | 189 | 禁用root登录的意义在于,"root登录"不是一个正常需求。 190 | 你不应当一直使用root干活,而是应当尽量以普通用户工作,直到需要sudo。 191 | 万一一直需要root,可以sudo bash。 192 | 193 | ------------------------------------------------- 194 | 195 | -> 设定~/.ssh <- 196 | 197 | 需要登录的,生成~/.ssh/id\_rsa 198 | 需要被登录的,添加~/.ssh/authorized\_keys 199 | ^ 200 | 201 | id\_rsa需要加密,具体用ssh-keygen -p -f ~/.ssh/id\_rsa修改密码 202 | ^ 203 | 204 | 使用ssh-agent可以简化密码输入,方法是启动ssh-agent,并将返回添加到环境变量中。 205 | 一般系统中默认会启动ssh-agent。 206 | ^ 207 | 208 | 启动时用ssh-add添加一项密钥,添加时输入密码。连接时不需要密码。 209 | ssh-add -L可以看到哪些被缓存了密码。 210 | ^ 211 | 212 | 确认authorized_keys,id_rsa,id_dsa的权限小于600。建议将整个.ssh全部700掉 213 | 214 | ------------------------------------------------- 215 | 216 | -> 设定ssh client config <- 217 | 218 | 设定control master 219 | ControlMaster auto 220 | ControlPath /tmp/ssh_mux_%h_%p_%r 221 | ControlPersist 10m 222 | ^ 223 | 224 | 优点是ssh断开时链接不断开,重复连接效率很高。 225 | 多次连接同一台机器也走同一根连接,不重复发起tcp连接。 226 | ^ 227 | 228 | 缺点是ssh出问题时,master连接很久才会自动断开。 229 | 需要ssh -O exit手工断开。 230 | 修改配置也是一样的。 231 | 例如ssh上去后,想再开一个ssh -L。就需要先ssh -O exit。 232 | ^ 233 | 234 | Protocol 2 235 | IgnoreRhosts yes 236 | ForwardAgent yes 237 | ^ 238 | 239 | (options)X11Forwarding no 240 | 241 | ------------------------------------------------- 242 | 243 | -> ssh的一些用法 <- 244 | 245 | 本地端口映射ssh -L localport:remoteip:remoteport。 246 | 将本地的localport端口映射到远程的remoteip:remoteport。 247 | 例如将本地的8080端口映射到www.google.com:80。 248 | 访问本地localhost:8080时,等于在目标机器上访问google。 249 | ^ 250 | 251 | 可以用于访问内部网络中的端口,基本是安全的。 252 | ^ 253 | 254 | 远程端口映射ssh -R remoteport:localip:localport。 255 | 将远程的remoteport映射到本地的localip:localport。 256 | 基本原理和-L类似,可以用于将本地服务公开到远程提供服务。 257 | 258 | ------------------------------------------------- 259 | 260 | -> 设定sysctl <- 261 | 262 | 在/etc/sysctl.d/下面放文件即可生效,不需要直接修改/etc/sysctl.conf。 263 | ^ 264 | 265 | 直接生效的方法。 266 | sudo sysctl -p /etc/sysctl.d/net.conf 267 | ^ 268 | 269 | 当然,保险的话在自己目录下写net.conf,生效,再复制到/etc/sysctl.d/下。 270 | ^ 271 | 272 | net.ipv4.tcp_congestion_control = htcp 273 | 拥塞控制算法,默认为cubic。如果是reno,请至少改成cubic。 274 | -------------------------------------------------------------------------------- /md/passwd.md: -------------------------------------------------------------------------------- 1 | # 密码强度分析 # 2 | 3 | ## 密码对抗的目标是穷举 ## 4 | 5 | 密码的意义在于,给我们一种方法区分不同的人:知道某些信息的,和不知道的。因此密码的信息量越大,区分两者的能力越好。 6 | 7 | 理论上说,对于不知道密码结构的攻击者,最多需要尝试多少次才能解开密码,其次数对二的对数即是密码的信息量。信息量越大的密码,在穷举攻击上难度越高。一个令人望而生畏的密码反而不会被穷举。 8 | 9 | 信息量是相对的,authentication这个单词有14个字母,看似强度很大(65-70bits)。但是一般攻击者都会试图用常用单词和变形表进行攻击,因此实际信息量的估计不超过英语总单词量(14bits以下)。更精确的说,密码的强度应当是攻击者选用的所有方法下的信息熵。从普遍角度来说,当然密码本身的信息越随机,强度越接近理论值,记忆越困难。 10 | 11 | ## 多少位密码是安全的 ## 12 | 13 | 多少位密码是安全的这个问题其实很难说,取决于两个因素。 14 | 15 | 1. 系统可以忍受多快的重试。 16 | 2. 丢失对你造成的损失。 17 | 18 | 一般来说,损失越大,能够接受的破解概率越低。例如你也许不会介意自己的订票密码被万分之一的概率破解,但是却未必能容忍自己的银行卡被万分之一的概率破解。 19 | 20 | 重试速度则取决于系统的性能,机制等。网络系统往往可以忍受1req/s的重试很长时间,一年的攻击量就是25bit左右。但是如果使用了抗重试算法则会降低到1req/min以下。 21 | 22 | --- 23 | 24 | 本地密码则更多的取决于性能。使用强力计算机并行计算往往可以提供1+Mreq/s的尝试,因此本地文件的密码一定要比网络密码强这是常识。 25 | 26 | 安全的密码至少要满足,你的密码强度比上单位时间的攻击量,大于你能够忍受的破解概率。 27 | 28 | 例如,时间长达一年,每秒攻击一次,一年可以实施25bits左右次的攻击。对于40bits的密码,破解概率就是1/30k(15bits=30k)。 29 | 30 | ## 推荐值 ## 31 | 32 | 网络上使用的密码,强度从低到高可以分为20/30/40/50/60/70几个档。 33 | 34 | 1. 20bits的密码可以用来保护一些并不重要的东西,例如电脑的登录密码(可以被强行重置),不常用的论坛(丢失无损失)。不知为何,银行卡密码是这一等级的。 35 | 2. 30bits的密码可以用来保护一些中等级别的东西。这个强度利于记忆,但是未必绝对安全。通常用于保护一些很常用,有一点保密意义,但是并不是特别在意的东西。例如家里的wifi密码。 36 | 3. 40bits是强密码的最低值。要保护有价信息,最低需要40bits的密码。例如邮箱。 37 | 4. 50bits是网络上的强密码,推荐用于保护高安全性机密信息,例如网银登录密码。或者用于保存一些本地不是特别重要的信息。 38 | 5. 60bits以上的密码在网络上已经没有意义了。这个级别是本地高安全信息的存放最低强度。 39 | 6. 70bits基本可以保证任何东西的安全。 40 | 41 | 如果要对抗政府机关,请选择128bits密钥。 42 | 43 | ## 字符的选择 ## 44 | 45 | 个人推荐,密码的选择范围为0-9a-z,即小写字母和数字。排除掉o和l。选择范围为34个,信息量5bits。 46 | 47 | 排除o和l是因为,某些字体下这两个会像0和1。当然你也可以排除掉0和1。 48 | 49 | 符号在某些键盘上会比较难输入,大写字符亦然。尤其在手机上,每输入一个大写都要额外的按Shift,因此相当于多了一次按键。而且在记忆上又需要加上“xx大写X”的前缀,记忆不便。 50 | 51 | ## 汉字密码的迷思 ## 52 | 53 | 汉字的选择是8000以上,所以可以提供13bits的强度。这样的密码,只要三个字就基本可以满足强密码。 54 | 55 | 听起来挺好,问题是汉字的输入长度,至少是2-3键一字。三个字差不多6-9键,和8个字符没什么区别。如果使用常用词组,还会降低安全性。 56 | 57 | 如果系统支持的话,对于中国人来说,使用汉字密码确实很方便,容易记忆。但是设计系统的时候最好不要扯到这种东西,容易出错而且不容易调试。 58 | 59 | ## 一个数学问题 ## 60 | 61 | 用户的密码有n种可能性,攻击者每次尝试的破解概率是多少呢?m次尝试的破解概率是多少呢? 62 | 63 | --- 64 | 65 | 如果你的答案是m/n,考虑一下,你每实验个几次用户就改一次密码会如何? 66 | 67 | 如果用户改密码足够频繁,答案应该是1-((n-1)/n)^m。直观点带入个数据看看,n=2,m=2。原本是100%破解出,现在只有75%。 68 | 69 | 如果n=1000000。当m=1000000时,实际破解概率只有六成出头。 70 | 71 | --- 72 | 73 | 上面的数学问题说明几点。 74 | 75 | 1. 定期改密对暴力破解有一定的抵抗能力,但是效果并不显著。 76 | 2. 要抵抗某个级别的暴力攻击,必须有相应级别的密码强度。靠频繁修改密码是没用的。 77 | 3. 定期改密对抗的对象是偶然的密码泄漏。 78 | 79 | ## 九宫连线的强度 ## 80 | 81 | 9! = 362880 = 18.5bits。其实会略高一些,因为还有P(8,9), P(7,9)等。但是大多数时候不会使用一些特别的绕法。所以大约是16-20bits,5-6位数字的强度。 82 | 83 | # 机制和强度 # 84 | 85 | ## 抗重试算法 ## 86 | 87 | 所谓抗重试算法,就是能在不干扰正常登录的情况下,降低重试者的频率。 88 | 89 | 最简单的算法是5次失败后锁定五分钟,这时的重试频率就降低到1req/min了。另一种方法是失败一次后double登录时间间隔。例如第一次失败后3秒内不能登录,第二次失败后10秒,类推。 90 | 91 | 抗重试算法的核心值在于失败次数和锁定时间。过长的锁定时间会使得攻击者可以人肉尝试你的登录,把正常用户锁在系统外面。过低的重试次数又会导致客户不慎输错后被锁定。而短的锁定时间和很高的重试次数则会抵消抗重试的效用。 92 | 93 | 另一些方案则采用验证码来对抗软件自动重试。 94 | 95 | ## 抗重试算法的几个细节 ## 96 | 97 | 首先应当注意,如果打开验证码不一定需要跟着打开失败锁定,但是如果打开失败锁定就必须打开验证码。否则有人会以高频进行重试,导致用户自己无法登录。 98 | 99 | 其次,注意验证的次序,首先检测验证码,然后检查是否锁定,最后检查是否登录成功。如果失败,返回原因一概是登录失败。从安全角度来说,最好不要说明问题的细节(例如是否存在用户)。否则容易造成信息泄漏。例如使用最简单密码尝试多个帐号,从返回中发现是帐号是否存在(登录失败或者不存在本用户)。或者先检测登录是否成功后检测验证码,登录失败时返回为“密码错误”而非“验证码错误”,导致虽然无法登录,但是可以继续暴力破解密码。 100 | 101 | --- 102 | 103 | 不要耍小聪明,用IP来限制重试。攻击者可能利用上万IP来把你的限制绕过去。要假定攻击者可以做到一些还算正常的异常事件。 104 | 105 | 不要把计数放到session中,攻击者可能通过放弃session的方式绕过。 106 | 107 | 另外,抗重试算法必须贯彻到系统的每个密码框上。改密的时候需要重新输入密码是常识吧。那里是否有抗重试?短信令牌是否做了抗重试? 108 | 109 | ## 验证码 ## 110 | 111 | 如果你的网站需要被某些残障人士使用,或者政府规定你的网站必须能够被这些人所用。那么单纯的图片验证码就会产生麻烦。 112 | 113 | 即使抛开上述不论,验证码也是个很麻烦的问题。太强的验证码会让用户失去耐性,太简单的则会让OCR绕过你的防御。 114 | 115 | 推荐的方法之一是使用reCAPTCHA。 116 | 117 | ## 警告 ## 118 | 119 | 警告是抗重试算法的升级。简单来说,除了维护一个锁定解除后失败计数外,还维护一个连续失败次数的计数。如果这个计数大于一定值,就给客户发邮件报警,强制客户设定更强级别的密码。 120 | 121 | 警告和调整可以有效的降低密码位数。例如我们能够忍受万分之一的破解概率,连续失败计数是1000次。那么简单来说,25bits的密码就足够保护系统的安全。因为攻击者试验了1000次后,密码强度就会提高到一个他无法接受的值,所以总破解概率只有1000/(2^25),约1/33000,正好在万分之一以内。 122 | 123 | --- 124 | 125 | 当然,实践中并不推荐这么低安全的密码。但是正确的警告可以大幅增强用户密码的安全性是毋庸置疑的。警告和调整的假定是,不是每个帐号都会被暴力攻击的。所以只需要提高正在被暴力攻击的账户的安全级别,而保持普通帐号的安全性恰好在忍受范围以内即可。 126 | 127 | 另外注意两个细节。 128 | 129 | 1. 记录每次登录,无论其成功失败。记录需要包含尽可能多的非涉密数据,例如登录来源,设备信息,帐号,是否成功。需要特别注意的是,不要记录失败密码。即使记录,也要使用加盐后的hash保存。 130 | 2. 通过记录找出一些恶意尝试的IP,并ban掉他们。 131 | 132 | ## 静默攻击 ## 133 | 134 | 有些设计的不正确的警告和调整算法容易被一种叫做“静默攻击”的方式攻击。具体来说,就是一定时间内将失败次数重置。这导致警告始终发不出去。 135 | 136 | 我们以银行卡密码为例,分析一下安全性。当然我假定银行卡磁条是会被漏出去的。 137 | 138 | 银行卡密码一般是6位,必须是数字,共计可以提供20bits的安全。一般一天可以失败五次(或者三次,但是下面的计算过程不变)。作为攻击者,你可以失败四次而不触发警报。一年就可以重试1460次,破解概率为1/685。考虑到一般密码是生日的变形,概率会更高一些。如果你的密码恰好是生日的变形,那么破解概率会升高到1/45。 139 | 140 | --- 141 | 142 | 这是一个不可接受的值。 143 | 144 | 而且最关键在于——银行系统不能提高密码强度。即使收到攻击警告也只能修改密码,或者干脆冻结户头。这样每次的破解概率始终固定,可以通过大量尝试来提升总体破解概率。即使不断修改密码,也只能让连续百万次攻击的成功率降低到63%。换言之,如果攻击者手里有七百张银行卡,每张卡每天试验4次密码,一年内有六成的概率猜到一个人的密码。考虑到很多人使用了生日作为密码,实际上风险会远远高出这个值。 145 | 146 | --- 147 | 148 | 正确设计的计数是不重置,或者以很长间隔重置的。一般来说,正常用户每年的错误概率是固定的。常用的密码不容易出错,不常用的密码很少输入。因此一个用户一年的输入错误数大约也就是几百,加上一些路过攻击,几千次出错一年是一个正常的值。 149 | 150 | 另外,这个计数不被密码重设所重置。 151 | 152 | ## 密码系统的缺陷 ## 153 | 154 | 密码系统本身有几个缺陷。 155 | 156 | 1. 在非安全环境中是容易泄漏的,keylogger是其天敌。即使是安全环境中,也容易被肩窥或者录像。或者可以换个说法,密码只对抗brute force。 157 | 2. 记忆困难,容易忘记。 158 | 3. 修改密码需要密码,一旦丢失就会失去账户的控制权。 159 | 160 | # 自动化密码重设机制 # 161 | 162 | ## 邮箱,SMS和电话 ## 163 | 164 | 自动化密码重设机制是上面第三个缺陷的解决方案。这种方案利用一个可以收取信息的工具来收取验证信息,从而证明你就是自己。 165 | 166 | 注意,这里必须保证“只有用户本人能够接收到用户本人的信息”。如果你的手机已经越狱,那么SMS并不总是可靠。而且大部分邮箱都使用明文发送报文内容,如果你的邮件服务商被人监听或者邮件服务商本身不可信的话,那么账户被盗是必然的。 167 | 168 | 理论上说,地址也是可以的,只要能够保证只有你自己能够接受到自己的信件,而且还能够接受缓慢的邮递时间。 169 | 170 | --- 171 | 172 | 这里要特别注意几点。 173 | 174 | 1. 如果用户的安全邮件地址是一个自己管理的域名的话,请确保这个域名本身是安全的。否则攻击者可以劫持域名的MX记录到他自己的邮件服务器,从而获得帐号。 175 | 2. 如果电话和SMS被配置了呼叫转移,邮箱被配置了转发的话,验证系统不攻自破。所以谨慎提防转发。 176 | 3. 由于大部分只能本人接收的系统都是明文的,而且其发送内容往往会被保留在某个地方很长时间。因此这些系统内只能发送一些短时效的一次性token,然后用户利用token来验证自己的身份。 177 | 178 | ## 安全问题 ## 179 | 180 | 早些年,自动化密码重设机制往往都伴有一个“安全问题”,只有答对了安全问题才能寻回密码。 181 | 182 | 这个的设计原因是因为,如果没有“安全问题”,只要始终去识别验证码就可以持续的发寻回密码邮件,会对系统造成干扰。而且在某些时候,邮箱会被短暂的破解。如果没有安全问题,帐号就会很快丢失。 183 | 184 | --- 185 | 186 | 但是安全问题的设计其实很扯淡——大部分时候,如果我能记得安全问题,我就能记得密码。实际上在大部分系统上的问题都是——压根不记得我的安全问题里面回答了什么。 187 | 188 | 而且现在随着SNS的流行,找到安全问题的答案越来越容易了——母亲的姓名和生日?小学老师的姓名?这些都不难被找到或者通过简单的社会工程学弄到。如果选择使用安全问题的话,一定不要选择填写一些公开或半公开信息,例如上面的亲属生日等等。因为很容易被找到。 189 | 190 | 当然,由于密码重置攻击的结果只是造成系统讨厌的垃圾邮件而已,所以使用这种攻击的人并不是特别多。至于“邮箱属于用户本人”基本已经成为了共识。大部分用户可以认同“邮箱被盗就等于所有账户被盗”。因此个人意见,安全问题不做也行。 191 | 192 | ## 慢重置 ## 193 | 194 | 同样是对暂时破解邮箱(或者手机——这更频繁)的解决,慢重置利用减缓重置速度的方法来抵抗攻击者。这个思路不仅适用于自动重置,而且适用于人工重置过程。 195 | 196 | 想象你被劫持了,绑匪弄不到你的银行卡密码,但是他们拥有你的身份证和银行卡。他们是否可以通过身份证代办的方式来重置你的密码(假定密码重置不需要本人亲自到场——很多时候本人亲自到场是有困难的)呢? 197 | 198 | --- 199 | 200 | 银行对抗这一问题的方法是,首先必须挂失。在挂失一周后才能重置密码。当然,在大部分业务里面一周太长了。但是长达小时级别的通告-重置周期是值得考虑的。几个小时的时间足够真正的用户看到重置动作通知,并做出合适的反应。立刻进行重置的话,用户不一定总是能看到。甚至在看到并产生反应的时间内,攻击者就造成了损失。 201 | 202 | 慢重置的问题在于对于重置的响应很慢,这会使得正常用户很不愉快。所以建议只有真正需要高安全性的系统才使用这种方案。 203 | 204 | ## 社会关系验证 ## 205 | 206 | 社会关系验证出名的有facebook和qq两家,其中qq的实现还是有问题的。 207 | 208 | 所谓社会关系验证,实际上就是从你的好友中挑选N个人,如果你能够证明其中M个确实是你的好友,你就可以通过验证。 209 | 210 | 证明的方法很多,例如你通过别的方法通知对方,让他们知道那个验证请求确实是你。facebook采用的是一堆图片里面找到哪个人是对方,有的时候碰到一堆乱标的图片会很火大。 211 | 212 | 注意,这里挑选N个人,必须是系统随机选择,而不能是客户自己来选择。否则就会产生M个好友一加就能抢掉你的帐号的问题。 213 | 214 | ## 自动化密码重设系统的缺陷 ## 215 | 216 | 自动化密码重设系统实际上等同于密码验证,因此如果自动化密码重设系统存在缺陷,密码系统再强也没用。所以自动化密码重设系统的强度必须大于或者等于密码验证。而实现上说,大部分系统的强度是远不及密码系统的。 217 | 218 | 从设计者角度来说,最好的办法就是不做自动化密码重置。以下是几种替代流程,当然,他们各有缺陷。 219 | 220 | 1. 通过用户的物理接触,核对一些信息,再触发密码寻回流程。这样的成本很高。 221 | 2. 预先发送的密码信封,里面封装了一个revoke token,可以触发寻回流程。这其实是安全问题的变形,但是更不容易猜出,也相对不容易丢失。但是一样需要线下机制辅助。 222 | 223 | 从用户来说一定要小心。随着你的手机和邮箱绑定了越来越多的寻回,一旦邮箱和手机丢失是一件非常恐怖的事情(尤其是手机,因为很多邮箱都绑定到了手机)。所以如果绑定了大量寻回,邮箱必须强密码,手机必须打开加密(丢失后整个机器无法读取),而且千万别越狱。 224 | 225 | # 增强验证 # 226 | 227 | ## TOTP ## 228 | 229 | 增强验证是试图解决密码的缺陷一,使得在非安全环境中密码即使泄漏也不会造成帐号被盗。大部分增强验证都是基于TOTP(time-based one time password)的。 230 | 231 | 首先,服务器和某个安全的设备共享一个密钥,并且保持时间同步。然后在登录时,安全的设备显示密钥加密过的当前时间,只要和服务器上的密钥加密过的当前时间在一定差以内,就算通过。 232 | 233 | 问题在于安全设备。目前gauth使用手机,这其实未必安全。真正的安全设备必须是特别设计实现的硬件,例如工行的电子令牌。 234 | 235 | ## 几个TOTP实例 ## 236 | 237 | google authentication就不说了,目前受到广泛应用。 238 | 239 | 国内做OTP有两家比较大的公司,坚石诚信和飞天诚信。 240 | 241 | yubico公司出品的yubikey是个很有趣的产品。这个产品一个U盘模样的硬件,把自己伪装成一块键盘。当用户需要输入OTP时,只需要按一下按钮,yubikey就会自动模拟按出对应的键。因此这个产品可以免去用户手工输入的繁琐,而且支持几乎所有操作系统。 242 | 243 | ## 利用自动化密码重设的机制来增强验证 ## 244 | 245 | 有些时候,我们可以利用自动化密码重设的机制来增强验证。例如登录时需要输入手机短信令牌等等。 246 | 247 | 实际上这种设计并无必要。因为自动化密码重设机制等效于密码本身,利用密码来增强密码是没有道理的。以手机短信令牌为例,我们可以想想。如果丢失手机,那么得到手机的人可以重置密码来完成登录。如果手机没有丢失(即手机始终是安全的),那么可以只用手机而不需要密码,强度并没有降低。正常情况下我们不这么做的理由是因为收SMS看手机会比较麻烦。可既然每次登录都必须输入验证码,为什么还需要密码呢? 248 | 249 | 一个比较可行的方案是,利用一种渠道来重置密码,而利用另一个来增强验证。那么这两个通讯渠道必须都保证安全。 250 | 251 | ## 客户证书 ## 252 | 253 | 客户证书可以为我们提供一个强验证机制,并且可以保证通讯过程中的安全。不过遗憾的是,大部分客户证书的实施开销都很高。 254 | 255 | 软件证书相对容易使用,但是很容易被从计算机中窃取。硬件证书可以规避窃取问题(如同TOTP一样),但是成本很高。而且大部分硬件证书都有兼容性问题,无法用于windows以外的系统,甚至IE以外的浏览器。 256 | 257 | 客户证书最麻烦的问题,在于大量客户证书的发行和管理很麻烦,而且通讯过程中的密码学计算需要大量资源。 258 | 259 | ## 增强验证的缺陷 ## 260 | 261 | 独立硬件的增强认证系统的麻烦在于,需要随身携带令牌防丢失。而且为了防止丢失——这几乎是必然的——所以需要一些额外的验证码作为丢失时的报废手段。而这些密码一旦丢失,其结果和令牌泄漏是一样的。不过幸好,大多数情况下都不需要输入紧急密码,更不会在非安全环境输入。 262 | 263 | 而且TOTP必须每次都看一下手机或者令牌(yubikey例外),这对需要频繁输入的密码是很不利的。 264 | 265 | # 从用户角度说应当怎么做 # 266 | 267 | ## 每个网站使用不同密码 ## 268 | 269 | 即使不对每个网站使用不同密码,至少也要将网站分组,使用不同密码。绝对不能对所有网站使用同一密码。 270 | 271 | 上文可以看到,不同密级的密码长度是不固定的。建议普通网站8个字符,高安全性网站10个字符,最高安全性网站12个字符。(每个字符5bits计) 272 | 273 | 对不提供防重试,警告功能的网站提升密码强度,至少要提升10个bits的安全性。 274 | 275 | ## 定期修改密码 ## 276 | 277 | 定期修改密码的主要目标是抵抗偶然的密码丢失,例如非安全环境,肩窥,或者偶然被人猜到。 278 | 279 | 周期取决于偶然事件发生的概率。即使没有什么你可以察觉的偶然事件,三年修改一次密码也是个良好的习惯。 280 | 281 | ## 避开01a ## 282 | 283 | 虽然听起来很愚蠢,但是大部分brute force程序都会从11xxxx或者00xxxx,aaxxxx开始枚举,而不是按照数字或者字母频度表排序。因此在密码的前面使用01a其实略微增加了被枚举的概率。 284 | 285 | ## 对可以打开OTP的服务启用OTP ## 286 | 287 | 以下是一些著名的使用了gauth的网站。 288 | 289 | * google 290 | * facebook 291 | * github 292 | * evernote 293 | * dropbox 294 | * ssh 295 | 296 | 以下是一些自己实现了OTP的例子: 297 | 298 | * 支付宝(宝令) 299 | 300 | ## 不引入无效的麻烦 ## 301 | 302 | 这个逻辑在“利用自动化密码重设的机制来增强验证”里面出现过。不需要用同等安全性的方式来加强安全性。 303 | 304 | 例如手机宝令和短信令牌。在手机安全的情况下,两者都是安全的。当手机不安全了(被植入程序或者丢失),两者都不安全。因此同时启用两者并不增强安全性,只增加麻烦。 305 | 306 | 实际上只要引入其中比较好用的一个就行了。相比宝令,短信受环境影响更大,因此建议关闭短信令牌,只启用宝令。 307 | 308 | ## 小心处理安全通讯方式 ## 309 | 310 | 主要说的是主邮箱和手机。用于安全性加强的联系方式往往会被用户配置为互相加强的情况,这是非常危险的。安全中最危险的模型就是环状模型,一旦一个点漏了整个环都会崩溃。 311 | 312 | 例如两个邮箱,A邮箱可以被B重置,B邮箱可以被A重置,又没有其他的保险措施。于是当其中任意一个发生泄漏的时候,所有的帐号就像被接到了一起的鞭炮一样,一个也跑不了。 313 | 314 | 通常而言,建议的方式是星型汇聚——多个安全的联系方式用同一个基础作为寻回。一般来说,就是多个邮箱利用同一个手机做寻回。或者多个安全方式之间不做寻回加强——因为反而不安全。 315 | 316 | --- 317 | 318 | 对于星型汇聚,当这个点出问题时所有帐号都会面临风险这点不变。但是当某个邮箱出问题时,其他邮箱并不面临风险,同时(可能)可以通过手机寻回密码。 319 | 320 | 对于不做寻回加强,万一邮箱被盗,相关帐号就无法恢复了。但是至少这风险不会蔓延到其他邮箱的帐号上。 321 | 322 | ——致命性标靶的面积总是越小越好。 323 | -------------------------------------------------------------------------------- /md/python-startup.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | python-startup 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 |
17 |
18 |
19 |

安装

20 | 21 |
22 |
23 |

windows

24 |

python.org下载对应的安装包。注意:

25 |
    26 |
  • python.org,不是com!!!
  • 27 |
  • gitit,不要download,会被墙
  • 28 |
29 |

最后,注意一点。python在windows下有相当多的兼容问题,例如编码(混帐windows)。

30 | 31 |
32 |
33 |

deb

34 |
aptitude install python
 35 | 
36 | 37 |
38 |
39 |

rpm

40 |

我也不知道,大概是yum install python

41 | 42 |
43 |
44 |

mac

45 |

自带。

46 | 47 |
48 |
49 |

python shell

50 |
    51 |
  • python
  • 52 |
  • ipython
  • 53 |
  • bpython
  • 54 |
  • DreamPie
  • 55 |
56 | 57 |
58 |
59 |

IDE

60 |
    61 |
  • ulipad
  • 62 |
  • pydev
  • 63 |
  • emacs
  • 64 |
65 | 66 |
67 |
68 |
69 |
70 |
71 |
72 |

program

73 | 74 |
75 |
76 |

hello, world

77 |
print 'hello, world'
 78 | 
79 | 80 |
81 |
82 |

varable

83 |
a = 1
 84 | b = '2'
 85 | c = a + b # BooBoo, error
 86 | c = str(a) + b
 87 | d = '12'
 88 | id(c) == id(d) # True
 89 | 
90 | 91 |
92 |
93 |

control

94 |
if a < b:
 95 |     do somethin
 96 | for i in range(10):
 97 |     print i
 98 | while True:
 99 |     died loop
100 | with the_people_you_love():
101 |     die
102 | 
103 | 104 |
105 |
106 |

function

107 |
def mul(a, b):
108 |     return a * b
109 | def mulmul(*p):
110 |     s = 1
111 |     for i in p: s = mul(s, i)
112 |     return s
113 | def yamm(*p):
114 |     return reduce(mul, p)
115 | 
116 | def f(a, b, c):
117 |     return a + c
118 | f(1, None, 2)
119 | f(1, c=2, b=0)
120 | def g(a, b=0, c=0):
121 |     return a + c
122 | f(1)
123 | f(1, c=2)
124 | 
125 | 126 |
127 |
128 |

class

129 |
class A(object):
130 |     b = 1
131 |     def __init__(self):
132 |         self.c = 2
133 | a = A()
134 | 
135 |

注意a.b和a.c的区别。

136 | 137 |
138 |
139 |

yield

140 |
for i in fib(n): print i
141 | 
142 | def fib1(n):
143 |     a, b, r = 1, 0, []
144 |     for i in xrange(n):
145 |         a, b = a+b, a
146 |         r.appand(a)
147 |     return r
148 | 
149 | def fib2(n):
150 |     a, b = 1, 0
151 |     for i in xrange(n):
152 |         a, b = a+b, a
153 |         yield a
154 | 
155 | def fib3(n):
156 |     def inner(i, a, b):
157 |         if i == n: return None, None
158 |         return lambda : inner(i+1, a+b, a), a+b
159 |     return inner(1, 1, 0)
160 | 
161 | l, a = fib3(10)
162 | while l:
163 |     print a
164 |     l, a = l()
165 | 
166 | 167 |
168 |
169 |
170 |
171 |
172 |
173 |

package management

174 | 175 |
176 |
177 |

pip

178 |
pip install virtualenv
179 | pip install -r requirements.txt
180 | 
181 | 182 |
183 |
184 |

virtualenv

185 |
virtualenv env
186 | 
187 | 188 |
189 |
190 |

常用包

191 |
    192 |
  • chardet
  • 193 |
  • numpy
  • 194 |
  • scipy
  • 195 |
  • sympy
  • 196 |
  • matplotlib
  • 197 |
  • pygments
  • 198 |
  • virtualenv
  • 199 |
  • mako
  • 200 |
  • sqlalchemy
  • 201 |
  • gevent
  • 202 |
  • Scapy
  • 203 |
  • pyzmq
  • 204 |
  • twisted
  • 205 |
  • werkzeug
  • 206 |
  • celery
  • 207 |
  • beautifulsoap
  • 208 |
  • lxml
  • 209 |
  • pyqt
  • 210 |
  • pyside
  • 211 |
  • pygame
  • 212 |
  • PIL
  • 213 |
214 | 215 |
216 |
217 |
218 |
219 |
220 |
221 |

web deploy

222 | 223 |
224 |
225 |

nginx

226 |
location /static {
227 |          index  index.html index.htm;
228 |          gzip   on;
229 | }
230 | 
231 | location ~ ^/(api|sys)/ {
232 |          include        uwsgi_params;
233 |          uwsgi_pass     unix:/run/uwsgi/app/app1/socket;
234 | }
235 | 
236 | 237 |
238 |
239 |

apache

240 |
<Location />
241 |     SetHandler uwsgi-handler
242 |     uWSGISocket 127.0.0.1:3031
243 | </Location>
244 | 
245 | 246 |
247 |
248 |

uwsgi

249 |
[uwsgi]
250 | uid             = web
251 | gid             = web
252 | chmod-socket    = 666
253 | 
254 | plugins         = python
255 | workers         = 1
256 | reload-on-as    = 196
257 | touch-reload    = /home/web/app1/RELEASE
258 | chdir           = /home/web/app1
259 | pythonpath      = /usr
260 | module          = main
261 | 
262 | 263 |
264 |
265 |

web.py

266 |
app = web.application(urls)
267 | 
268 | if web.config.get('sesssion') is None:
269 |     web.config.session = web.session.Session(
270 |         app, web.session.DBStore(web.config.db, 'sessions'))
271 | 
272 | if __name__ == '__main__':
273 |     if len(sys.argv) > 1:
274 |         cmd = sys.argv.pop(1)
275 |         if cmd == 'profile':
276 |             app.run(web.profiler)
277 |         elif cmd == 'test':
278 |             from test import tester
279 |             tester.testall(app)
280 |     else: app.run()
281 | else: application = app.wsgifunc()
282 | 
283 | 284 |
285 |
286 |

django

287 |
uwsgi --chdir=/path/to/your/project \
288 |     --module=mysite.wsgi:application \
289 |     --env DJANGO_SETTINGS_MODULE=mysite.settings \
290 |     --master --pidfile=/tmp/project-master.pid \
291 |     --socket=127.0.0.1:49152 \      # can also be a file
292 |     --processes=5 \                 # number of worker processes
293 |     --uid=1000 --gid=2000 \         # if root, uwsgi can drop privileges
294 |     --harakiri=20 \                 # respawn processes taking more than 20 seconds
295 |     --limit-as=128 \                # limit the project to 128 MB
296 |     --max-requests=5000 \           # respawn processes after serving 5000 requests
297 |     --vacuum \                      # clear environment on exit
298 |     --home=/path/to/virtual/env \   # optional path to a virtualenv
299 |     --daemonize=/var/log/uwsgi/yourproject.log      # background the process
300 | 
301 | 302 |
303 |
304 |

dispatcher

305 |
    306 |
  • Quixote

  • 307 |
  • django

    308 |

    urlpatterns = patterns('', 309 | # Examples: 310 | # url(r'^$', '{{ projectname }}.views.home', name='home'), 311 | # url(r'^{{ projectname }}/', include('{{ project_name }}.foo.urls')),

    312 |
    # Uncomment the admin/doc line below to enable admin documentation:
    313 | # url(r'^admin/doc/', include('django.contrib.admindocs.urls')),
    314 | 
    315 | 
    316 | # Uncomment the next line to enable the admin:
    317 | url(r'^admin/', include(admin.site.urls)),
    318 | 
    319 |

    )

  • 320 |
  • web.py

    321 |

    urls = ( 322 | '/openid/', 'actions.webgid.host', 323 | '/ticket/', 'actions.ticket.ticket', 324 | '/timezone/', 'actions.timezone.timezone', 325 | '/segment/', 'actions.segtxt.segtxt', 326 | '/txtimg', txtimg.app, 327 | '/pycode', pycode.app, 328 | )

  • 329 |
  • bottle

    330 |

    from bottle import route, run, template

    331 |

    @route('/hello/:name') 332 | def index(name='World'): 333 | return template('Hello {{name}}!', name=name)

    334 |

    run(host='localhost', port=8080)

  • 335 |
  • flask

    336 |

    from flask import Flask 337 | app = Flask(name)

    338 |

    @app.route("/") 339 | def hello(): 340 | return "Hello World!"

    341 |

    if name == "main": 342 | app.run()

  • 343 |
344 | 345 |
346 |
347 |

ORM

348 |

ORM有用么?

349 | 350 |
351 |
352 |

template

353 |
    354 |
  • mako

    355 |

    <%inherit file="base.html"/> 356 | <% 357 | rows = [[v for v in range(0,10)] for row in range(0,10)] 358 | %>

    359 | 360 | % for row in rows: 361 | ${makerow(row)} 362 | % endfor 363 |
    364 |

    <%def name="makerow(row)"> 365 | 366 | % for name in row: 367 | ${name}\ 368 | % endfor 369 | 370 |

    371 |

    from mako.template import Template 372 | print Template("hello ${data}!").render(data="world")

  • 373 |
  • jinja2

    374 |

    {% extends "layout.html" %} 375 | {% block body %} 376 |

    381 | {% endblock %}

    382 |

    from jinja2 import Template 383 | template = Template(...) 384 | template.render(name='John Doe')

  • 385 |
386 | 387 |
388 |
389 |

session

390 | 391 |
392 |
393 |
394 |
395 | 396 | 397 | 423 | 424 | 425 | -------------------------------------------------------------------------------- /md/python-startup.md: -------------------------------------------------------------------------------- 1 | # 安装 # 2 | 3 | ## windows ## 4 | 5 | 去[python.org](http://python.org/getit)下载对应的安装包。注意: 6 | 7 | * python.org,不是com!!! 8 | * gitit,不要download,会被墙 9 | 10 | 最后,注意一点。python在windows下有相当多的兼容问题,例如编码(混帐windows)。 11 | 12 | ## deb ## 13 | 14 | aptitude install python 15 | 16 | ## rpm ## 17 | 18 | 我也不知道,大概是`yum install python`。 19 | 20 | ## mac ## 21 | 22 | 自带。 23 | 24 | ## python shell ## 25 | 26 | * python 27 | * ipython 28 | * bpython 29 | * DreamPie 30 | 31 | ## IDE ## 32 | 33 | * ulipad 34 | * pydev 35 | * emacs 36 | 37 | # program # 38 | 39 | ## hello, world ## 40 | 41 | print 'hello, world' 42 | 43 | ## varable ## 44 | 45 | a = 1 46 | b = '2' 47 | c = a + b # BooBoo, error 48 | c = str(a) + b 49 | d = '12' 50 | id(c) == id(d) # True 51 | 52 | ## control ## 53 | 54 | if a < b: 55 | do somethin 56 | for i in range(10): 57 | print i 58 | while True: 59 | died loop 60 | with the_people_you_love(): 61 | die 62 | 63 | ## function ## 64 | 65 | def mul(a, b): 66 | return a * b 67 | def mulmul(*p): 68 | s = 1 69 | for i in p: s = mul(s, i) 70 | return s 71 | def yamm(*p): 72 | return reduce(mul, p) 73 | 74 | def f(a, b, c): 75 | return a + c 76 | f(1, None, 2) 77 | f(1, c=2, b=0) 78 | def g(a, b=0, c=0): 79 | return a + c 80 | f(1) 81 | f(1, c=2) 82 | 83 | ## class ## 84 | 85 | class A(object): 86 | b = 1 87 | def __init__(self): 88 | self.c = 2 89 | a = A() 90 | 91 | 注意a.b和a.c的区别。 92 | 93 | ## yield ## 94 | 95 | for i in fib(n): print i 96 | 97 | def fib1(n): 98 | a, b, r = 1, 0, [] 99 | for i in xrange(n): 100 | a, b = a+b, a 101 | r.appand(a) 102 | return r 103 | 104 | def fib2(n): 105 | a, b = 1, 0 106 | for i in xrange(n): 107 | a, b = a+b, a 108 | yield a 109 | 110 | def fib3(n): 111 | def inner(i, a, b): 112 | if i == n: return None, None 113 | return lambda : inner(i+1, a+b, a), a+b 114 | return inner(1, 1, 0) 115 | 116 | l, a = fib3(10) 117 | while l: 118 | print a 119 | l, a = l() 120 | 121 | # package management # 122 | 123 | ## pip ## 124 | 125 | pip install virtualenv 126 | pip install -r requirements.txt 127 | 128 | ## virtualenv ## 129 | 130 | virtualenv env 131 | 132 | ## 常用包 ## 133 | 134 | * chardet 135 | * numpy 136 | * scipy 137 | * sympy 138 | * matplotlib 139 | * pygments 140 | * virtualenv 141 | * mako 142 | * sqlalchemy 143 | * gevent 144 | * Scapy 145 | * pyzmq 146 | * twisted 147 | * werkzeug 148 | * celery 149 | * beautifulsoap 150 | * lxml 151 | * pyqt 152 | * pyside 153 | * pygame 154 | * PIL 155 | 156 | # web deploy # 157 | 158 | ## nginx ## 159 | 160 | location /static { 161 | index index.html index.htm; 162 | gzip on; 163 | } 164 | 165 | location ~ ^/(api|sys)/ { 166 | include uwsgi_params; 167 | uwsgi_pass unix:/run/uwsgi/app/app1/socket; 168 | } 169 | 170 | ## apache ## 171 | 172 | 173 | SetHandler uwsgi-handler 174 | uWSGISocket 127.0.0.1:3031 175 | 176 | 177 | ## uwsgi ## 178 | 179 | [uwsgi] 180 | uid = web 181 | gid = web 182 | chmod-socket = 666 183 | 184 | plugins = python 185 | workers = 1 186 | reload-on-as = 196 187 | touch-reload = /home/web/app1/RELEASE 188 | chdir = /home/web/app1 189 | pythonpath = /usr 190 | module = main 191 | 192 | ## web.py ## 193 | 194 | app = web.application(urls) 195 | 196 | if web.config.get('sesssion') is None: 197 | web.config.session = web.session.Session( 198 | app, web.session.DBStore(web.config.db, 'sessions')) 199 | 200 | if __name__ == '__main__': 201 | if len(sys.argv) > 1: 202 | cmd = sys.argv.pop(1) 203 | if cmd == 'profile': 204 | app.run(web.profiler) 205 | elif cmd == 'test': 206 | from test import tester 207 | tester.testall(app) 208 | else: app.run() 209 | else: application = app.wsgifunc() 210 | 211 | ## django ## 212 | 213 | uwsgi --chdir=/path/to/your/project \ 214 | --module=mysite.wsgi:application \ 215 | --env DJANGO_SETTINGS_MODULE=mysite.settings \ 216 | --master --pidfile=/tmp/project-master.pid \ 217 | --socket=127.0.0.1:49152 \ # can also be a file 218 | --processes=5 \ # number of worker processes 219 | --uid=1000 --gid=2000 \ # if root, uwsgi can drop privileges 220 | --harakiri=20 \ # respawn processes taking more than 20 seconds 221 | --limit-as=128 \ # limit the project to 128 MB 222 | --max-requests=5000 \ # respawn processes after serving 5000 requests 223 | --vacuum \ # clear environment on exit 224 | --home=/path/to/virtual/env \ # optional path to a virtualenv 225 | --daemonize=/var/log/uwsgi/yourproject.log # background the process 226 | 227 | ## dispatcher ## 228 | 229 | * Quixote 230 | 231 | * django 232 | 233 | urlpatterns = patterns('', 234 | # Examples: 235 | # url(r'^$', '{{ project_name }}.views.home', name='home'), 236 | # url(r'^{{ project_name }}/', include('{{ project_name }}.foo.urls')), 237 | 238 | # Uncomment the admin/doc line below to enable admin documentation: 239 | # url(r'^admin/doc/', include('django.contrib.admindocs.urls')), 240 | 241 | # Uncomment the next line to enable the admin: 242 | url(r'^admin/', include(admin.site.urls)), 243 | ) 244 | 245 | * web.py 246 | 247 | urls = ( 248 | '/openid/', 'actions.webgid.host', 249 | '/ticket/', 'actions.ticket.ticket', 250 | '/timezone/', 'actions.timezone.timezone', 251 | '/segment/', 'actions.segtxt.segtxt', 252 | '/txtimg', txtimg.app, 253 | '/pycode', pycode.app, 254 | ) 255 | 256 | * bottle 257 | 258 | from bottle import route, run, template 259 | 260 | @route('/hello/:name') 261 | def index(name='World'): 262 | return template('Hello {{name}}!', name=name) 263 | 264 | run(host='localhost', port=8080) 265 | 266 | * flask 267 | 268 | from flask import Flask 269 | app = Flask(__name__) 270 | 271 | @app.route("/") 272 | def hello(): 273 | return "Hello World!" 274 | 275 | if __name__ == "__main__": 276 | app.run() 277 | 278 | ## ORM ## 279 | 280 | ORM有用么? 281 | 282 | ## template ## 283 | 284 | * mako 285 | 286 | <%inherit file="base.html"/> 287 | <% 288 | rows = [[v for v in range(0,10)] for row in range(0,10)] 289 | %> 290 | 291 | % for row in rows: 292 | ${makerow(row)} 293 | % endfor 294 |
295 | 296 | <%def name="makerow(row)"> 297 | 298 | % for name in row: 299 | ${name}\ 300 | % endfor 301 | 302 | 303 | 304 | from mako.template import Template 305 | print Template("hello ${data}!").render(data="world") 306 | 307 | * jinja2 308 | 309 | {% raw %} 310 | {% extends "layout.html" %} 311 | {% block body %} 312 | 317 | {% endblock %} 318 | 319 | from jinja2 import Template 320 | template = Template(...) 321 | template.render(name='John Doe') 322 | {% endraw %} 323 | 324 | ## session ## 325 | -------------------------------------------------------------------------------- /md/qiniu_deepin.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | qiniu_deepin 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 |
17 |
18 |

授权

19 |

cc-by-sa 3.0 cn en

20 | 21 |
22 |
23 |
24 |
25 |
26 |

自我介绍

27 |
    28 |
  • python程序员
  • 29 |
  • C/C++程序员
  • 30 |
  • linux深度用户 31 |
      32 |
    • debian系用户
    • 33 |
  • 34 |
35 |

36 | 37 |
38 |
39 | 40 |

41 | 42 |
43 |
44 | 45 |

咳咳,刚刚那张不知道为什么有新浪的LOGO,让我们忽略他。

46 |

47 | 48 |
49 |
50 |
51 |
52 |
53 |

七牛简介

54 |
    55 |
  • 2011年创立于上海
  • 56 |
  • 做开发者的数据在线托管,加速,处理
  • 57 |
58 | 59 |
60 |
61 |
62 |
63 |
64 |

云存储的意义

65 |
    66 |
  • 备份
  • 67 |
  • 高可用
  • 68 |
  • 弹性费用计算
  • 69 |
  • CDN
  • 70 |
71 | 72 |
73 |
74 | 75 |
    76 |
  • 备份 77 |
      78 |
    • 备份策略
    • 79 |
    • 如何保证备份成功
    • 80 |
  • 81 |
  • 高可用 82 |
      83 |
    • 多机+盘阵
    • 84 |
    • 多节点同步
    • 85 |
    • 其他方案
    • 86 |
  • 87 |
  • 弹性费用计算 88 |
      89 |
    • 为峰值留出预量
    • 90 |
  • 91 |
  • CDN
  • 92 |
93 | 94 |
95 |
96 |
97 |
98 |
99 |

七牛云存储的特色

100 |
    101 |
  • UGC 102 |
      103 |
    • 减少带宽占用,加速用户上传
    • 104 |
    • 提供回调
    • 105 |
  • 106 |
  • 数据处理 107 |
      108 |
    • 不只是图片
    • 109 |
  • 110 |
  • 丰富的sdk 111 |
      112 |
    • C...
    • 113 |
    • C#
    • 114 |
    • Java
    • 115 |
    • nodejs
    • 116 |
    • php
    • 117 |
    • ruby
    • 118 |
    • python
    • 119 |
    • ...
    • 120 |
  • 121 |
122 | 123 |
124 |
125 |
126 |
127 |

图片处理

128 |
    129 |
  • 基本信息获取
  • 130 |
  • EXIF信息获取
  • 131 |
  • 缩略图
  • 132 |
  • 裁剪,旋转
  • 133 |
  • 水印
  • 134 |
135 | 136 |
137 |
138 |
139 |
140 |

流媒体处理

141 |
    142 |
  • 音频转码
  • 143 |
  • 视频转码
  • 144 |
  • 视频缩略图
  • 145 |
  • HTTP Live Streaming
  • 146 |
147 | 148 |
149 |
150 |
151 |
152 |

文档转换

153 |
    154 |
  • markdown转html
  • 155 |
156 | 157 |
158 |
159 |
160 |
161 |

其他

162 |
    163 |
  • 数据处理组合
  • 164 |
  • 生成qr码
  • 165 |
  • 结果持久化
  • 166 |
  • 用户提供的任意处理代码
  • 167 |
168 | 169 |
170 |
171 |
172 |
173 |

SDK library

174 |
    175 |
  • 全部开源
  • 176 |
  • MIT授权 177 |
      178 |
    • 可以链接到商业项目中
    • 179 |
    • 可以加入Powered by qiniu
    • 180 |
  • 181 |
  • 随时update,支持最新feature
  • 182 |
  • 使用github,鼓励用户提交pull request
  • 183 |
184 | 185 |
186 |
187 |
188 |
189 |

SDK utils

190 |
    191 |
  • qrsync
  • 192 |
193 | 194 |
195 |
196 |
197 |
198 |

实例一,本文档上传

199 |
    200 |
  • 申请帐号(10G空间,10G下载,免费),获得ak和sk
  • 201 |
  • 创建空间xuzhixiang
  • 202 |
  • 建立配置文件qiniu.json
  • 203 |
  • 下载七牛工具组
  • 204 |
  • 执行qrsync qiniu.json
  • 205 |
  • 结果在这里
  • 206 |
207 | 208 |
209 |
210 |
211 |
212 |

实例二,文章开头的图片裁剪

213 |
    214 |
  • 原始图片地址:http://xuzhixiang.u.qiniudn.com/photo/shell01.jpg
  • 215 |
  • 裁剪图片地址:http://xuzhixiang.u.qiniudn.com/photo/shell01.jpg?imageMogr/crop/!300x250a150a0
  • 216 |
217 | 218 |
219 |
220 |
221 |
222 |

实例二,以html方式阅读本文档

223 |

url的结尾加上md2html试试

224 | 225 |
226 |
227 |
228 |
229 |

实例三,经过reveal渲染后的文档

230 |
    231 |
  • 执行自己写的工具md2html
  • 232 |
  • 再次执行qrsync qiniu.json,内容会差量上传
  • 233 |
  • 直接访问这里,可以看到结果
  • 234 |
235 | 236 |
237 |
238 |
239 |
240 |

感谢观赏

241 |

本文可以猛击这里

242 |

或者扫这里

243 | 244 |
245 |
246 |
247 | 248 | 249 | 275 | 276 | 277 | -------------------------------------------------------------------------------- /md/qiniu_deepin.md: -------------------------------------------------------------------------------- 1 | # 授权 # 2 | 3 | cc-by-sa 3.0 [cn](http://creativecommons.org/licenses/by-sa/3.0/cn/) [en](http://creativecommons.org/licenses/by-sa/3.0/) 4 | 5 | # 自我介绍 # 6 | 7 | * python程序员 8 | * C/C++程序员 9 | * linux深度用户 10 | * debian系用户 11 | 12 | ![](http://xuzhixiang.u.qiniudn.com/photo/aboutme.png) 13 | 14 | --- 15 | 16 | ![](http://xuzhixiang.u.qiniudn.com/photo/shell01.jpg) 17 | 18 | --- 19 | 20 | 咳咳,刚刚那张不知道为什么有新浪的LOGO,让我们忽略他。 21 | 22 | ![](http://xuzhixiang.u.qiniudn.com/photo/shell01.jpg?imageMogr/crop/!300x250a150a0) 23 | 24 | # 七牛简介 # 25 | 26 | * 2011年创立于上海 27 | * 做开发者的数据在线托管,加速,处理 28 | 29 | # 云存储的意义 # 30 | 31 | * 备份 32 | * 高可用 33 | * 弹性费用计算 34 | * CDN 35 | 36 | --- 37 | 38 | * 备份 39 | * 备份策略 40 | * 如何保证备份成功 41 | * 高可用 42 | * 多机+盘阵 43 | * 多节点同步 44 | * 其他方案 45 | * 弹性费用计算 46 | * 为峰值留出预量 47 | * CDN 48 | 49 | # 七牛云存储的特色 # 50 | 51 | * UGC 52 | * 减少带宽占用,加速用户上传 53 | * 提供回调 54 | * 数据处理 55 | * 不只是图片 56 | * 丰富的sdk 57 | * C... 58 | * C# 59 | * Java 60 | * nodejs 61 | * php 62 | * ruby 63 | * python 64 | * ... 65 | 66 | # 图片处理 # 67 | 68 | * 基本信息获取 69 | * EXIF信息获取 70 | * 缩略图 71 | * 裁剪,旋转 72 | * 水印 73 | 74 | # 流媒体处理 # 75 | 76 | * 音频转码 77 | * 视频转码 78 | * 视频缩略图 79 | * HTTP Live Streaming 80 | 81 | # 文档转换 # 82 | 83 | * markdown转html 84 | 85 | # 其他 # 86 | 87 | * 数据处理组合 88 | * 生成qr码 89 | * 结果持久化 90 | * 用户提供的任意处理代码 91 | 92 | # SDK library # 93 | 94 | * 全部开源 95 | * MIT授权 96 | * 可以链接到商业项目中 97 | * 可以加入Powered by qiniu 98 | * 随时update,支持最新feature 99 | * 使用github,鼓励用户提交pull request 100 | 101 | # SDK utils # 102 | 103 | * qrsync 104 | 105 | # 实例一,本文档上传 # 106 | 107 | * 申请帐号(10G空间,10G下载,免费),获得ak和sk 108 | * 创建空间xuzhixiang 109 | * 建立配置文件qiniu.json 110 | * 下载[七牛工具组](http://docs.qiniu.com/tools/v6/qrsync.html) 111 | * 执行`qrsync qiniu.json` 112 | * 结果在[这里](http://xuzhixiang.u.qiniudn.com/slide/qiniu_deepin.md?md2html) 113 | 114 | # 实例二,文章开头的图片裁剪 # 115 | 116 | * 原始图片地址:http://xuzhixiang.u.qiniudn.com/photo/shell01.jpg 117 | * 裁剪图片地址:http://xuzhixiang.u.qiniudn.com/photo/shell01.jpg?imageMogr/crop/!300x250a150a0 118 | 119 | # 实例二,以html方式阅读本文档 # 120 | 121 | 在[url](http://xuzhixiang.u.qiniudn.com/slide/qiniu_deepin.md)的结尾加上md2html[试试](http://xuzhixiang.u.qiniudn.com/slide/qiniu_deepin.md?md2html)。 122 | 123 | # 实例三,经过reveal渲染后的文档 # 124 | 125 | * 执行自己写的工具[md2html](https://github.com/shell909090/utils/blob/master/md2slide) 126 | * 再次执行`qrsync qiniu.json`,内容会差量上传 127 | * 直接访问[这里](http://xuzhixiang.u.qiniudn.com/slide/qiniu_deepin.html),可以看到结果 128 | 129 | # 感谢观赏 # 130 | 131 | 本文可以猛击[这里](http://xuzhixiang.u.qiniudn.com/slide/qiniu_deepin.html) 132 | 133 | ![或者扫这里](http://xuzhixiang.u.qiniudn.com/slide/qiniu_deepin.html?qrcode) 134 | -------------------------------------------------------------------------------- /md/schemepy.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | schemepy 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 |
17 |
18 |

授权

19 |

cc-by-sa 3.0 cn en

20 | 21 |
22 |
23 |
24 |
25 |
26 |

解析

27 |

lisp系列语言的解析器很简单,简单里说,就是匹配括号,形成树。这个在任何一本算法书上都有。有三点细节,一个是引号。在字符串结束前,不需要匹配括号。而且字符串内部还要处理转义。一个是注释,从分号到行结束。最后一个是引用,单引号后面不需要空格。

28 | 29 |
30 |
31 |

编译

32 |

解析的结果是python对象,要转换为scheme对象才方便使用。为了简单起见,不需要特别处理的对象,实际上是同功能的python对象来兼容的,包括int,long,float。同时,某些string对象实际上会被转换为其他对象,例如#t和#f。

33 |

scheme对象的核心是OCons,在类lisp语言中,序对被广泛用于列表,树的定义。

34 | 35 |
36 |
37 |
38 |
39 |
40 |
41 |

环境

42 |

执行是lisp的核心。lisp语言的执行有两个对象,一个是执行环境,一个是栈。

43 |

所谓执行环境,就是保存符号和对象对应关系的地方。从语言模型来说,这是一个层级模型。符号应当首先在最内层查找,当内层无法找到对象时逐层向外查找,直到找到为止。

44 |
(define (fib n)
 45 |   (define (fib1 i a b)
 46 |     (if (= i n)
 47 |         a
 48 |         (fib1 (+ i 1) (+ a b) a)))
 49 |   (fib1 1 1 1))
 50 | (display (fib 1))
 51 | (display (fib 2))
 52 | (display (fib 3))
 53 | (display (fib 4))
 54 | (display (fib 5))
 55 | 
56 | 57 |
58 |
59 |

函数调用和子环境

60 |

所谓子环境,就是在函数内部有一个自有的子执行环境,保存所有内部的执行结果和中间变量。在每个子函数执行的时候,都应当定义一个子环境。更广泛的说,在每个函数,let的内部,,或者其他需要保存自有的执行环境的地方,都有一个子环境。

61 |

子环境还和函数调用密切相关。调用时的形参和实参对应,实际上是在子环境内,将实参的对象赋值给形参符号的过程。理解了这点,就很容易理解动态参数和命名参数的处理方法。

62 | 63 |
64 |
65 |

闭包和环境树

66 |

闭包依赖于子环境又不止于子环境。为了支持闭包,每个生成的函数对象必须保存他生成时的环境栈。

67 |

由于环境需要逐层向上查找,而且环境内又会嵌套环境。因此最终环境会构成类似于树的结构。但是和通常的树相反,树的指向并不是从根到每个叶子,而是从叶子到根。

68 | 69 |
70 |
71 |

快速查找

72 |

上述算法是理论算法,为了查找一个对象,需要在多个子环境中进行查询,其效率是O(n),n是环境深度。在层数比较多的时候,这个会严重拖慢系统速度。

73 |

我们可以想象,对于某个子环境来说,有哪些符号,绑定到哪些值,相对都是固定的,很少调整。因此,查找是频繁操作,而写入不是。对于这种情况,我们可以用缓存来增加查找速度。

74 | 75 |
76 |
77 |
78 |
79 |
80 |
81 |

82 | 83 |
84 |
85 |

尾递归

86 |

是否支持尾递归是一个非常重要的特性,因为不支持尾递归的语言,不可以用递归解一些问题。例如fib问题的单递归解法,在不支持尾递归的系统里面很容易跑到栈溢出。

87 | 88 |
89 |
90 |

trampoline

91 |

为了支持尾递归,有几种可选做法。我采用了一种叫做trampoline的做法,配合自行实现的栈来实现尾递归。要特别说明的是,为了支持尾递归,系统的执行效率必然有一些下降。这主要是因为在python语言中,list的效率比系统栈更低,而我又不愿意为了这事专门优化一个特殊的list。

92 |

trampoline的核心思想是。在执行某个单次调用时,约定返回分为两种值,普通结果和特殊值。当返回结果是普通结果时,将最上层的待执行对象退栈。当结果为特殊值时,不对执行对象退栈。因此相反,在执行过程中可以对执行栈进行压栈。这样就可以形成三种情况:

93 |
    94 |
  • 返回普通值:调用返回
  • 95 |
  • 执行过程中压栈,返回特殊值:调用
  • 96 |
  • 执行过程中退栈,再压入新的执行对象,配合返回特殊值:转跳/尾递归
  • 97 |
98 | 99 |
100 |
101 |

传递变量

102 |

为了达到对普通求值过程的透明,我设计了一个当前传递变量r。并约定tuple为特殊值。任何对象的执行结果不允许为tuple,除非经过特殊包裹。当用户返回一个tuple时,r = r[0]来获得真实的传递变量值。

103 |

当返回普通值时,r为返回值。这个值在下次执行栈顶对象时会被传入,作为调用的返回结果。当r返回tuple时,这个值则是作为调用/转跳/尾递归传递值,传递给执行对象。

104 |

因此,从具体函数的编写角度来考虑,第一次从系统中取得的r值是上层函数所传递的。当函数需要递归调用时,调用栈的call方法,栈会将目标对象压栈,并返回tuple。将tuple返回trampoline函数,就会完成调用过程。

105 |

然后就是相对不好理解的return过程。当栈求值完成时,执行对象会再次执行。传递的参数就不是上层函数的值了,而是调用的返回。

106 | 107 |
108 |
109 |
110 |
111 |
112 |
113 |

执行挂起

114 |

当我们自行控制了执行环境和栈之后,我们就可以在执行的任何时候,将环境和栈序列化,再将来的特定时候重新加载再进行计算。这个任何时候,包括某些需要等待IO输入输出的时候,也可以是等待用户输入的时候。

115 |

从简单来说,这是coroutine,但是没有设计多个上下文切换和调度的功能。从复杂来说,这赋予了程序在某个python环境中执行到一半,代码挂起。在合适的条件下,在另一个python环境中被恢复运行。对此,scheme代码无需感知到差异。

116 |

例如下面的例子:

117 |
(display "user input:")
118 | (let ((s (raw_input)))
119 |     (when
120 |      ((= s "q") (quit))
121 |      ((= s "t") (display "temp file size"))
122 |      (#t (quit))
123 |      ))
124 | 
125 |

其中,raw_input的时候,程序会暂停执行,在web界面上弹出提示框让用户选择。当用户选择了之后,程序才会继续执行。而代码对此一无所知。

126 |

具体的实现上,使用了一种特殊的异常。当触发异常时,就会触发trampoline进行核心转储。从理论上,这个赋予了任何代码状态保留和调试的能力。当然,有些时候保存是无法再执行的,例如已经做了压栈但是环境传递变量还没有跟着更新的时候。

127 | 128 |
129 |
130 |

coroutine

131 |

为了完成coroutine,至少还需要一个调度核心。调度核心需要维护一个(或者两个)表,分别记录所有被挂起的上下文,和其中那些已经就绪。具体关于调度核心的内容,可以参考操作系统书中调度队列的描述。

132 | 133 |
134 |
135 |
136 |
137 |
138 |
139 |

函数调用

140 | 141 |
142 |
143 |

函数调用过程

144 |

在lisp中调用一个函数非常辛苦的。由于函数是第一类对象,因此可能出现((lambda (x) (* x x)) 10)的语句。因此首先,当希望调用一个函数的时候,必须先对函数本身求值。

145 |

当函数的值已经确定时,我们又要区分两种情况——正则序对象和应用序对象。在scheme里,这个又被称为严格的和非严格的。大部分对象而言,计算应当是严格的。在所有参数计算完成后才进入函数的体。而为了定义内部符号,有部分计算必须是非严格的,例如if。如果将两个分支分别执行完成后才调用if的体,那就没有任何意义了。

146 |

因此,在解释器中有一个标志位,分别判断是否需要严格执行。

147 |

对于严格执行的对象,另一个麻烦是参数求值顺序。一个是右到左求值,另一个是左到右。在实际的代码中,我采用的是右到左顺序。在初始化时将list反转,结果直接append list就可以完成参数的计算。

148 | 149 |
150 |
151 |

内部符号

152 |

由于可以定义非严格计算对象,因此我们才可能将所有内部符号的实现独立于scheme执行和解析系统。

153 |

内部符号是在一个叫做builtin的环境中定义的。在任意执行环境中,这是一切的根。因此用户可以定义自己的符号来覆盖系统符号,例如定义自己的if(如果可以解决非严格问题的话)。由于在系统内还没有引入宏,因此实际上是定义不出合法的if的。

154 | 155 |
156 |
157 |
158 |
159 |
160 |
161 |

调试

162 |

trampoline给予了我们一个很方便的调试接口——在每次系统对栈顶求值之前,调用一个调试接口。这个接口就等效于单步中断。在系统中,我实现了两个最简单的调试器——print_step和Debuger。前者会将每次的中间栈状态和传递变量打印出来,便于我们跟踪程序。后者可以显示栈,环境,符号,传递变量,监视栈的增长和退出。不过由于尾递归的特性,这并不等于可以监视调用。基本上,除了breakpoint外,这个调试工具实现了一个调试器所需要的所有功能。

163 | 164 |
165 |
166 |

打印和格式化

167 |

为了能够调试,我们对必须可以将对象打印出来。出于简单化的理由,我希望打印出来的源码是可读和可执行的。就是说,将一段代码写好后,执行打印出来的代码和执行源代码应当是完全相同的。

168 |

为此,我设计了调试打印系统,这个系统的执行逻辑和编译相反,将转换的目标树格式化为合适的代码。

169 |

同时,由于对代码格式化的规范性,因此可以将不整齐的代码通过parser读入,从format中打印出去,达到格式化的效果。

170 | 171 |
172 |
173 |
174 |
175 | 176 | 177 | 203 | 204 | 205 | -------------------------------------------------------------------------------- /md/schemepy.md: -------------------------------------------------------------------------------- 1 | # 授权 # 2 | 3 | cc-by-sa 3.0 [cn](http://creativecommons.org/licenses/by-sa/3.0/cn/) [en](http://creativecommons.org/licenses/by-sa/3.0/) 4 | 5 | # 解析 # 6 | 7 | lisp系列语言的解析器很简单,简单里说,就是匹配括号,形成树。这个在任何一本算法书上都有。有三点细节,一个是引号。在字符串结束前,不需要匹配括号。而且字符串内部还要处理转义。一个是注释,从分号到行结束。最后一个是引用,单引号后面不需要空格。 8 | 9 | ## 编译 ## 10 | 11 | 解析的结果是python对象,要转换为scheme对象才方便使用。为了简单起见,不需要特别处理的对象,实际上是同功能的python对象来兼容的,包括int,long,float。同时,某些string对象实际上会被转换为其他对象,例如#t和#f。 12 | 13 | scheme对象的核心是OCons,在类lisp语言中,序对被广泛用于列表,树的定义。 14 | 15 | # 环境 # 16 | 17 | 执行是lisp的核心。lisp语言的执行有两个对象,一个是执行环境,一个是栈。 18 | 19 | 所谓执行环境,就是保存符号和对象对应关系的地方。从语言模型来说,这是一个层级模型。符号应当首先在最内层查找,当内层无法找到对象时逐层向外查找,直到找到为止。 20 | 21 | (define (fib n) 22 | (define (fib1 i a b) 23 | (if (= i n) 24 | a 25 | (fib1 (+ i 1) (+ a b) a))) 26 | (fib1 1 1 1)) 27 | (display (fib 1)) 28 | (display (fib 2)) 29 | (display (fib 3)) 30 | (display (fib 4)) 31 | (display (fib 5)) 32 | 33 | ## 函数调用和子环境 ## 34 | 35 | 所谓子环境,就是在函数内部有一个自有的子执行环境,保存所有内部的执行结果和中间变量。在每个子函数执行的时候,都应当定义一个子环境。更广泛的说,在每个函数,let的内部,,或者其他需要保存自有的执行环境的地方,都有一个子环境。 36 | 37 | 子环境还和函数调用密切相关。调用时的形参和实参对应,实际上是在子环境内,将实参的对象赋值给形参符号的过程。理解了这点,就很容易理解动态参数和命名参数的处理方法。 38 | 39 | ## 闭包和环境树 ## 40 | 41 | 闭包依赖于子环境又不止于子环境。为了支持闭包,每个生成的函数对象必须保存他生成时的环境栈。 42 | 43 | 由于环境需要逐层向上查找,而且环境内又会嵌套环境。因此最终环境会构成类似于树的结构。但是和通常的树相反,树的指向并不是从根到每个叶子,而是从叶子到根。 44 | 45 | ## 快速查找 ## 46 | 47 | 上述算法是理论算法,为了查找一个对象,需要在多个子环境中进行查询,其效率是O(n),n是环境深度。在层数比较多的时候,这个会严重拖慢系统速度。 48 | 49 | 我们可以想象,对于某个子环境来说,有哪些符号,绑定到哪些值,相对都是固定的,很少调整。因此,查找是频繁操作,而写入不是。对于这种情况,我们可以用缓存来增加查找速度。 50 | 51 | # 栈 # 52 | 53 | ## 尾递归 ## 54 | 55 | 是否支持尾递归是一个非常重要的特性,因为不支持尾递归的语言,不可以用递归解一些问题。例如fib问题的单递归解法,在不支持尾递归的系统里面很容易跑到栈溢出。 56 | 57 | ## trampoline ## 58 | 59 | 为了支持尾递归,有几种可选做法。我采用了一种叫做trampoline的做法,配合自行实现的栈来实现尾递归。要特别说明的是,为了支持尾递归,系统的执行效率必然有一些下降。这主要是因为在python语言中,list的效率比系统栈更低,而我又不愿意为了这事专门优化一个特殊的list。 60 | 61 | trampoline的核心思想是。在执行某个单次调用时,约定返回分为两种值,普通结果和特殊值。当返回结果是普通结果时,将最上层的待执行对象退栈。当结果为特殊值时,不对执行对象退栈。因此相反,在执行过程中可以对执行栈进行压栈。这样就可以形成三种情况: 62 | 63 | * 返回普通值:调用返回 64 | * 执行过程中压栈,返回特殊值:调用 65 | * 执行过程中退栈,再压入新的执行对象,配合返回特殊值:转跳/尾递归 66 | 67 | ## 传递变量 ## 68 | 69 | 为了达到对普通求值过程的透明,我设计了一个当前传递变量r。并约定tuple为特殊值。任何对象的执行结果不允许为tuple,除非经过特殊包裹。当用户返回一个tuple时,r = r[0]来获得真实的传递变量值。 70 | 71 | 当返回普通值时,r为返回值。这个值在下次执行栈顶对象时会被传入,作为调用的返回结果。当r返回tuple时,这个值则是作为调用/转跳/尾递归传递值,传递给执行对象。 72 | 73 | 因此,从具体函数的编写角度来考虑,第一次从系统中取得的r值是上层函数所传递的。当函数需要递归调用时,调用栈的call方法,栈会将目标对象压栈,并返回tuple。将tuple返回trampoline函数,就会完成调用过程。 74 | 75 | 然后就是相对不好理解的return过程。当栈求值完成时,执行对象会再次执行。传递的参数就不是上层函数的值了,而是调用的返回。 76 | 77 | # 执行挂起 # 78 | 79 | 当我们自行控制了执行环境和栈之后,我们就可以在执行的任何时候,将环境和栈序列化,再将来的特定时候重新加载再进行计算。这个任何时候,包括某些需要等待IO输入输出的时候,也可以是等待用户输入的时候。 80 | 81 | 从简单来说,这是coroutine,但是没有设计多个上下文切换和调度的功能。从复杂来说,这赋予了程序在某个python环境中执行到一半,代码挂起。在合适的条件下,在另一个python环境中被恢复运行。对此,scheme代码无需感知到差异。 82 | 83 | 例如下面的例子: 84 | 85 | (display "user input:") 86 | (let ((s (raw_input))) 87 | (when 88 | ((= s "q") (quit)) 89 | ((= s "t") (display "temp file size")) 90 | (#t (quit)) 91 | )) 92 | 93 | 其中,raw_input的时候,程序会暂停执行,在web界面上弹出提示框让用户选择。当用户选择了之后,程序才会继续执行。而代码对此一无所知。 94 | 95 | 具体的实现上,使用了一种特殊的异常。当触发异常时,就会触发trampoline进行核心转储。从理论上,这个赋予了任何代码状态保留和调试的能力。当然,有些时候保存是无法再执行的,例如已经做了压栈但是环境传递变量还没有跟着更新的时候。 96 | 97 | ## coroutine ## 98 | 99 | 为了完成coroutine,至少还需要一个调度核心。调度核心需要维护一个(或者两个)表,分别记录所有被挂起的上下文,和其中那些已经就绪。具体关于调度核心的内容,可以参考操作系统书中调度队列的描述。 100 | 101 | # 函数调用 # 102 | 103 | ## 函数调用过程 ## 104 | 105 | 在lisp中调用一个函数非常辛苦的。由于函数是第一类对象,因此可能出现`((lambda (x) (* x x)) 10)`的语句。因此首先,当希望调用一个函数的时候,必须先对函数本身求值。 106 | 107 | 当函数的值已经确定时,我们又要区分两种情况——正则序对象和应用序对象。在scheme里,这个又被称为严格的和非严格的。大部分对象而言,计算应当是严格的。在所有参数计算完成后才进入函数的体。而为了定义内部符号,有部分计算必须是非严格的,例如if。如果将两个分支分别执行完成后才调用if的体,那就没有任何意义了。 108 | 109 | 因此,在解释器中有一个标志位,分别判断是否需要严格执行。 110 | 111 | 对于严格执行的对象,另一个麻烦是参数求值顺序。一个是右到左求值,另一个是左到右。在实际的代码中,我采用的是右到左顺序。在初始化时将list反转,结果直接append list就可以完成参数的计算。 112 | 113 | ## 内部符号 ## 114 | 115 | 由于可以定义非严格计算对象,因此我们才可能将所有内部符号的实现独立于scheme执行和解析系统。 116 | 117 | 内部符号是在一个叫做builtin的环境中定义的。在任意执行环境中,这是一切的根。因此用户可以定义自己的符号来覆盖系统符号,例如定义自己的if(如果可以解决非严格问题的话)。由于在系统内还没有引入宏,因此实际上是定义不出合法的if的。 118 | 119 | # 调试 # 120 | 121 | trampoline给予了我们一个很方便的调试接口——在每次系统对栈顶求值之前,调用一个调试接口。这个接口就等效于单步中断。在系统中,我实现了两个最简单的调试器——print_step和Debuger。前者会将每次的中间栈状态和传递变量打印出来,便于我们跟踪程序。后者可以显示栈,环境,符号,传递变量,监视栈的增长和退出。不过由于尾递归的特性,这并不等于可以监视调用。基本上,除了breakpoint外,这个调试工具实现了一个调试器所需要的所有功能。 122 | 123 | ## 打印和格式化 ## 124 | 125 | 为了能够调试,我们对必须可以将对象打印出来。出于简单化的理由,我希望打印出来的源码是可读和可执行的。就是说,将一段代码写好后,执行打印出来的代码和执行源代码应当是完全相同的。 126 | 127 | 为此,我设计了调试打印系统,这个系统的执行逻辑和编译相反,将转换的目标树格式化为合适的代码。 128 | 129 | 同时,由于对代码格式化的规范性,因此可以将不整齐的代码通过parser读入,从format中打印出去,达到格式化的效果。 130 | -------------------------------------------------------------------------------- /md/why_python.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | why_python 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 |
17 |
18 |
19 |

语言选型的愿望

20 |

作为任何一家公司,在语言选型上都有几个期望。

21 |
    22 |
  1. 易学
  2. 23 |
  3. 开发效率高
  4. 24 |
  5. 执行效率高
  6. 25 |
  7. 完备的社区
  8. 26 |
  9. 用户众多
  10. 27 |
  11. 可维护性好
  12. 28 |
29 | 30 |
31 |
32 | 33 |

不幸的是,大部分语言在上述目标中互相冲突。

34 |
    35 |
  • C语言用户众多,可维护性好,执行效率高,而且社区完备。但是难学,而且开发效率差到死。
  • 36 |
  • java语言用户众多,执行效率高,社区完备。可是可维护性差,开发效率也没高多少。
  • 37 |
  • C++的用户和执行效率都不错,社区完备。可是学起来超级难,维护性差到死,开发效率不好说。
  • 38 |
39 | 40 |
41 |
42 |
43 |
44 |
45 |
46 |

初创企业的选型要点

47 |
    48 |
  1. 最重要的是活下来。
  2. 49 |
  3. 不要考虑维护性,活不下来的产品不需要维护。
  4. 50 |
  5. 用户数是个问题,不过足够易学就可以弥补。两者至少有一个。
  6. 51 |
  7. 开发和执行效率是永恒的矛盾。两者只能选一个。
  8. 52 |
  9. 社区一定要完备。
  10. 53 |
54 | 55 |
56 |
57 | 58 |

初创企业的估算误差来由:

59 |
    60 |
  • 不靠谱的团队。人员离职比例和试用期接近,大量的时间消耗在培训和沟通上。
  • 61 |
  • 还没彻底搞明白自己想做什么。出一样东西三个月,又扔了。
  • 62 |
63 |

因此,初创企业最重要的是,开发效率足够高。尽快做出原型,尽快推出产品。

64 | 65 |
66 |
67 | 68 |

综合一下,初创企业选型的要点:

69 |
    70 |
  1. 尽量用自己熟悉的技术,或者有现成的解决方案。
  2. 71 |
  3. 语言开发效率高。
  4. 72 |
  5. 易学,或者用户数量大。
  6. 73 |
  7. 社区完备。
  8. 74 |
75 | 76 |
77 |
78 | 79 |

由于最后一个,我们只要检查一下所有社区完备的开发语言,然后挑选一种合适的就好。

80 |
    81 |
  • BASIC,开发效率高,易学
  • 82 |
  • bash,开发效率高,不易学,不易维护
  • 83 |
  • C,不要谈开发效率
  • 84 |
  • php,开发效率高,用户多,不易维护
  • 85 |
  • .net,恶心
  • 86 |
  • java,开发效率不高
  • 87 |
  • python,开发效率高,易学易维护
  • 88 |
  • ruby,开发效率高,易学易维护
  • 89 |
90 | 91 |
92 |
93 | 94 |

所以,最后看下来最佳选择是python和ruby,如果可以用basic也可以考虑。次之考虑bash和php。最后考虑java和net。

95 |

python和ruby的选择上。如果你的业务大部分和网页有关,用ruby,反之,用python。因为ruby的社区积累在页面开发上,而且有数种很好用的框架,用户群也是以极限的开发速度为导向的。在这点上用python没什么太大好处。

96 |

初创企业的一大误区是过高考虑了维护性和效率。除非你非常非常确定自己需要的是维护性和效率,否则不要想太多。

97 | 98 |
99 |
100 |
101 |
102 |
103 |
104 |

混编是个坑,跳不跳的过去各凭本事

105 |

我们新产品上使用的语言:

106 |
    107 |
  • C
  • 108 |
  • lua
  • 109 |
  • python
  • 110 |
  • scheme
  • 111 |
  • go
  • 112 |
113 |

各种语言执行自己合适的功能,无论从效率还是开发效率上都显得非常完美。

114 | 115 |
116 |
117 | 118 |

问题:

119 |
    120 |
  1. 初创企业哪里来个人会这么多语言。C是肯定会的,python不难学。可是要找个人玩的好lua, scheme和go就不是很容易了。
  2. 121 |
  3. 就算有几个人能搞定,后面进人维护怎么办?招不到人啊。所以以后无论工作量膨胀到多大,都是他们干?而且他们不能辞职不能生病连休假都要看情况?
  4. 122 |
  5. 万一出了问题,这几个人能搞定么?搞不定怎么办?
  6. 123 |
124 |

所以,混编听起来很美,其实是个大坑。跳不跳的过去,各自凭本事。如果没把握,尽量少用几种语言。

125 | 126 |
127 |
128 | 129 |

推荐的混编模式:

130 |
    131 |
  • python搞定大部分问题
  • 132 |
  • C提升效率和嵌入
  • 133 |
134 |

一种易学,一种大部分程序员都会。维护的困难性大大降低。

135 | 136 |
137 |
138 |
139 |
140 |
141 |

python效率一定低么

142 |
    143 |
  • 大部分时候,效率只和模型有关。用错假定,想的太多,算法错误,代码粗糙是效率低下的四大理由。和语言基本没什么关系。
  • 144 |
  • 有些效率问题不要着急解决。先算一下,目前解决这个效率问题要扔多少人工进去。如果人工比机器还贵,就买好的机器或者升级服务拖延。
  • 145 |
  • 有大规模数据运算?把这部分代码单独拆个模块,用cython写,然后编译。效率和C相差无几。
  • 146 |
  • 瓶颈在哪里?有的时候慢的不是python,而是某个库。换一个就好了。
  • 147 |
148 | 149 |
150 |
151 |
152 | 153 | 154 | 180 | 181 | 182 | -------------------------------------------------------------------------------- /md/why_python.md: -------------------------------------------------------------------------------- 1 | # 语言选型的愿望 # 2 | 3 | 作为任何一家公司,在语言选型上都有几个期望。 4 | 5 | 1. 易学 6 | 2. 开发效率高 7 | 3. 执行效率高 8 | 4. 完备的社区 9 | 5. 用户众多 10 | 6. 可维护性好 11 | 12 | --- 13 | 14 | 不幸的是,大部分语言在上述目标中互相冲突。 15 | 16 | * C语言用户众多,可维护性好,执行效率高,而且社区完备。但是难学,而且开发效率差到死。 17 | * java语言用户众多,执行效率高,社区完备。可是可维护性差,开发效率也没高多少。 18 | * C++的用户和执行效率都不错,社区完备。可是学起来超级难,维护性差到死,开发效率不好说。 19 | 20 | # 初创企业的选型要点 # 21 | 22 | 1. 最重要的是活下来。 23 | 2. 不要考虑维护性,活不下来的产品不需要维护。 24 | 3. 用户数是个问题,不过足够易学就可以弥补。两者至少有一个。 25 | 4. 开发和执行效率是永恒的矛盾。两者只能选一个。 26 | 5. 社区一定要完备。 27 | 28 | --- 29 | 30 | 初创企业的估算误差来由: 31 | 32 | * 不靠谱的团队。人员离职比例和试用期接近,大量的时间消耗在培训和沟通上。 33 | * 还没彻底搞明白自己想做什么。出一样东西三个月,又扔了。 34 | 35 | 因此,初创企业最重要的是,开发效率足够高。尽快做出原型,尽快推出产品。 36 | 37 | --- 38 | 39 | 综合一下,初创企业选型的要点: 40 | 41 | 1. 尽量用自己熟悉的技术,或者有现成的解决方案。 42 | 2. 语言开发效率高。 43 | 3. 易学,或者用户数量大。 44 | 4. 社区完备。 45 | 46 | --- 47 | 48 | 由于最后一个,我们只要检查一下所有社区完备的开发语言,然后挑选一种合适的就好。 49 | 50 | * BASIC,开发效率高,易学 51 | * bash,开发效率高,不易学,不易维护 52 | * C,不要谈开发效率 53 | * php,开发效率高,用户多,不易维护 54 | * .net,恶心 55 | * java,开发效率不高 56 | * python,开发效率高,易学易维护 57 | * ruby,开发效率高,易学易维护 58 | 59 | --- 60 | 61 | 所以,最后看下来最佳选择是python和ruby,如果可以用basic也可以考虑。次之考虑bash和php。最后考虑java和net。 62 | 63 | python和ruby的选择上。如果你的业务大部分和网页有关,用ruby,反之,用python。因为ruby的社区积累在页面开发上,而且有数种很好用的框架,用户群也是以极限的开发速度为导向的。在这点上用python没什么太大好处。 64 | 65 | 初创企业的一大误区是过高考虑了维护性和效率。除非你非常非常确定自己需要的是维护性和效率,否则不要想太多。 66 | 67 | # 混编是个坑,跳不跳的过去各凭本事 # 68 | 69 | 我们新产品上使用的语言: 70 | 71 | * C 72 | * lua 73 | * python 74 | * scheme 75 | * go 76 | 77 | 各种语言执行自己合适的功能,无论从效率还是开发效率上都显得非常完美。 78 | 79 | --- 80 | 81 | 问题: 82 | 83 | 1. 初创企业哪里来个人会这么多语言。C是肯定会的,python不难学。可是要找个人玩的好lua, scheme和go就不是很容易了。 84 | 2. 就算有几个人能搞定,后面进人维护怎么办?招不到人啊。所以以后无论工作量膨胀到多大,都是他们干?而且他们不能辞职不能生病连休假都要看情况? 85 | 3. 万一出了问题,这几个人能搞定么?搞不定怎么办? 86 | 87 | 所以,混编听起来很美,其实是个大坑。跳不跳的过去,各自凭本事。如果没把握,尽量少用几种语言。 88 | 89 | --- 90 | 91 | 推荐的混编模式: 92 | 93 | * python搞定大部分问题 94 | * C提升效率和嵌入 95 | 96 | 一种易学,一种大部分程序员都会。维护的困难性大大降低。 97 | 98 | # python效率一定低么 # 99 | 100 | * 大部分时候,效率只和模型有关。用错假定,想的太多,算法错误,代码粗糙是效率低下的四大理由。和语言基本没什么关系。 101 | * 有些效率问题不要着急解决。先算一下,目前解决这个效率问题要扔多少人工进去。如果人工比机器还贵,就买好的机器或者升级服务拖延。 102 | * 有大规模数据运算?把这部分代码单独拆个模块,用cython写,然后编译。效率和C相差无几。 103 | * 瓶颈在哪里?有的时候慢的不是python,而是某个库。换一个就好了。 104 | -------------------------------------------------------------------------------- /pdf/GFW.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shell909090/slides/d8aa123521daedee78e85e06f80c621929afaebd/pdf/GFW.pdf -------------------------------------------------------------------------------- /pdf/elisp.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shell909090/slides/d8aa123521daedee78e85e06f80c621929afaebd/pdf/elisp.pdf -------------------------------------------------------------------------------- /pdf/linux_fs.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shell909090/slides/d8aa123521daedee78e85e06f80c621929afaebd/pdf/linux_fs.pdf -------------------------------------------------------------------------------- /pdf/linux_on_net.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shell909090/slides/d8aa123521daedee78e85e06f80c621929afaebd/pdf/linux_on_net.pdf -------------------------------------------------------------------------------- /pdf/one_way_cross_gfw.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shell909090/slides/d8aa123521daedee78e85e06f80c621929afaebd/pdf/one_way_cross_gfw.pdf -------------------------------------------------------------------------------- /pdf/python_source.odp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shell909090/slides/d8aa123521daedee78e85e06f80c621929afaebd/pdf/python_source.odp -------------------------------------------------------------------------------- /pdf/python_source.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shell909090/slides/d8aa123521daedee78e85e06f80c621929afaebd/pdf/python_source.pdf -------------------------------------------------------------------------------- /pdf/step into cryptography.odp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shell909090/slides/d8aa123521daedee78e85e06f80c621929afaebd/pdf/step into cryptography.odp -------------------------------------------------------------------------------- /pdf/step into cryptography.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shell909090/slides/d8aa123521daedee78e85e06f80c621929afaebd/pdf/step into cryptography.pdf -------------------------------------------------------------------------------- /rir/cli.md: -------------------------------------------------------------------------------- 1 | %title: Run It Remote - PyCon2015 2 | %author: Shell.Xu 3 | %date: 2015-09-06 4 | 5 | 6 | 7 | 8 | -> Run It Remote <- 9 | 10 | ------------------------------------------------- 11 | 12 | -> 做什么用的 <- 13 | 14 | 用将代码放到远程执行。大概就是这样子: 15 | 16 | scp script.py host: 17 | ssh host python script.py 18 | 19 | ------------------------------------------------- 20 | 21 | -> 为什么要用rir <- 22 | 23 | 脚本的远程执行往往伴随几个困难。 24 | 25 | 目标机器在很深的内网,需要scp后再scp。 26 | ^ 27 | 更新脚本内容后需要重新copy。 28 | ^ 29 | 一次启动多个节点形成集群,更新代码后需要全部关闭再全部启动。 30 | ^ 31 | 没有api接口。 32 | ^ 33 | 一样工作,重复劳动。 34 | 35 | ------------------------------------------------- 36 | 37 | -> Live show <- 38 | 39 | A B C 40 | \^ \^ \^ 41 | | | | 42 | \\--jumpbox--/ 43 | | 44 | user 45 | 46 | ------------------------------------------------- 47 | 48 | -> rir能做什么 <- 49 | 50 | * 本地可以将任意函数放到远程执行。 51 | ^ 52 | * 支持远程import服务端上的库。 53 | ^ 54 | * 无缝支持py/pyc。 55 | ^ 56 | * 只有py的情况下不需要在远程写文件,只有进程,不触发任何警报。 57 | ^ 58 | * 动态链接库需要保证本地文件在远程可以导入(架构和依赖OK)。 59 | ^ 60 | * 支持远程打开本地文件,本地stdout/stderr。远程logging打印回本地。 61 | ^ 62 | * 支持远程回调本地函数。 63 | 64 | ------------------------------------------------- 65 | 66 | -> 怎么做的 <- 67 | 68 | 1. ssh host python -c '...',在远程执行一个引导代码。 69 | 获得stdin/stdout。 70 | ^ 71 | 2. 将核心脚本发送到远程执行,在importer上挂钩子,在stdout上挂钩子。 72 | ^ 73 | 3. 在本地和远程交互,发送指令要求执行。 74 | 75 | ------------------------------------------------- 76 | 77 | -> 更多细节 <- 78 | 79 | rir支持自定义方法获取远程管道,和自定义协议和远程通讯。 80 | 目前内置的方法全是ssh家族的。 81 | 82 | ^ 83 | * 支持自定义远程启动方法。 84 | ^ 85 | * ssh子进程 + pipe交互。 86 | ^ 87 | * 远程可以sudo(这是自然)来获得root权限。 88 | ^ 89 | * paramiko,无需启动子进程。 90 | ^ 91 | * 支持自定义通讯协议。 92 | ^ 93 | * raw data。marshal + zlib。size leaded frame。 94 | ^ 95 | * base64 coded。marshal + zlib + base64。 96 | 支持穿越一些比较特殊的过滤设备。 97 | ^ 98 | 99 | 实践中使用base64 coded模式,加上特殊的跳板机启动方案。 100 | 在七牛的集群上执行任意代码。 101 | 主要用来检查远程的漏洞修补情况。 102 | 103 | ------------------------------------------------- 104 | 105 | -> 还有什么 <- 106 | 107 | 发挥你的想象力。 108 | ^ 109 | 原则上,可以用pexpect启动一个ssh来获得远程管道。 110 | ^ 111 | 连接堡垒机,模拟输入,登录到目标机器。 112 | ^ 113 | 再模拟手工执行python的过程,将stdin/stdout转交给rir。 114 | ^ 115 | 一切就像真人一样。 116 | ^ 117 | 118 | 在远程执行任意代码,基本不会触发关键字。 119 | ^ 120 | 如果不想被解出来,我们还有。。。 121 | ^ 122 | diffie-hellman key exchange algorithm + aes-256-cfb embed. 123 | ^ 124 | of cause, you will got fired. 125 | ^ 126 | but they have no evidence about what you did... 127 | 128 | ------------------------------------------------- 129 | 130 | -> rir可以做到什么 <- 131 | 132 | 1. 在不同构的机器上执行运维脚本。 133 | (脚本需要是python,且在多个机器上都兼容) 134 | ^ 135 | 2. 免去部署过程,测试服务端/客户端s的执行。 136 | ^ 137 | 3. \*\*和\*\*\*也是很好用的,你懂。 138 | 139 | ------------------------------------------------- 140 | 141 | 142 | 143 | -> 根本上RIR是一个远程执行框架 <- 144 | 145 | ^ 146 | 你可以拿去自high 147 | 148 | ------------------------------------------------- 149 | 150 | -> 效率呢? <- 151 | 152 | 不算太高。在国内的用例里,每个的执行时间都到了秒级。 153 | 将一个web项目发到远程执行甚至用了2s。 154 | ^ 155 | 所以,如果有大量的设备(例如上千),还是老老实实用salt比较实在。 156 | ^ 157 | 但是对一次性应用来说,几秒的时间完全不痛不痒。 158 | 159 | ------------------------------------------------- 160 | 161 | -> 内附脚本 <- 162 | 163 | hwinfo: 收集远程机器的各种数据。 164 | sync: 同步远程文件。 165 | 166 | ------------------------------------------------- 167 | 168 | -> sync? <- 169 | 170 | 没错。sync又是一个非常复杂的小系统。 171 | 172 | ^ 173 | 使用yaml作为配置和数据存储。 174 | ^ 175 | 定义远程的一组特定目录。 176 | ^ 177 | 从远程同步文件内容和属性/属主到本地。 178 | ^ 179 | 只同步需要更新的,相同内容不需要通过网络。 180 | ^ 181 | 从本地同步文件内容和属性/属主到远程。 182 | 183 | ------------------------------------------------- 184 | 185 | -> 所以呢?sync是做什么的? <- 186 | 187 | 例如你有20台机器,家里有几台,自己有几台,帮朋友管几台。 188 | 每个系统都不一样,配置各自不一样。你打算如何管理配置? 189 | ^ 190 | sync可以将远程配置和属性批量拉到本地,再提交到git里。 191 | ^ 192 | 支持多人对同样的机器组进行协作更新。merge配置,pull,push。。。 193 | ^ 194 | 你们懂。 195 | ^ 196 | 反正push到机器上的时候不要一起来就行。 197 | 198 | ------------------------------------------------- 199 | 200 | -> Live show <- 201 | 202 | ------------------------------------------------- 203 | 204 | -> 效率呢? <- 205 | 206 | 7台内网机器+2台国内物理机。 207 | ^ 208 | 1.5s。 209 | ^ 210 | 其实一台机器同步一些配置文件下来并不需要多久。 211 | 只要你的机器能并发执行。。。 212 | 213 | ------------------------------------------------- 214 | 215 | -> License <- 216 | 217 | Run It Remote: BSD-3-clause 218 | This slide: cc-by-sa3.0 219 | 220 | ^ 221 | Powered by [MDP](https://github.com/visit1985/mdp) 222 | GPLv3 223 | 224 | ------------------------------------------------- 225 | 226 | 227 | 228 | 229 | -> Q&A <- 230 | -------------------------------------------------------------------------------- /rir/hwinfo.rec: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shell909090/slides/d8aa123521daedee78e85e06f80c621929afaebd/rir/hwinfo.rec -------------------------------------------------------------------------------- /rir/noapt.rec: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shell909090/slides/d8aa123521daedee78e85e06f80c621929afaebd/rir/noapt.rec -------------------------------------------------------------------------------- /rir/pycon.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | pycon 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 |
17 |
18 |

run it remote

19 | 20 |
21 |
22 |
23 |
24 |

做什么用的

25 |

用将代码放到远程执行。大概就是这样子:

26 |
scp script.py host:
 27 | ssh host python script.py
 28 | 
29 | 30 |
31 |
32 |
33 |
34 |

为什么要用rir

35 |

脚本的远程执行往往伴随几个困难。

36 |
    37 |
  1. 目标机器在很深的内网,需要scp后再scp。
  2. 38 |
  3. 更新脚本内容后需要重新copy。
  4. 39 |
  5. 一次启动多个节点形成集群,更新代码后需要全部关闭再全部启动。
  6. 40 |
  7. 没有api接口。
  8. 41 |
  9. 一样工作,重复劳动。
  10. 42 |
43 | 44 |
45 |
46 |
47 |
48 |

rir live show

49 | 50 |
51 |
52 |
53 |
54 |

rir做了什么

55 |
    56 |
  • 本地可以将任意函数放到远程执行。
  • 57 |
  • 支持远程import服务端上的库。 58 |
      59 |
    • 无缝支持py/pyc。
    • 60 |
    • 只有py的情况下不需要在远程写文件,只有进程,不触发任何警报。
    • 61 |
    • so需要保证本地文件在远程可以导入(架构和依赖OK)。
    • 62 |
  • 63 |
  • 支持远程打开本地文件,本地stdout/stderr。远程logging打印回本地。
  • 64 |
  • 支持远程回调本地函数。
  • 65 |
66 | 67 |
68 |
69 |
70 |
71 |

怎么做的

72 |
    73 |
  1. ssh host python -c '...',在远程执行一个引导代码。 74 | 获得stdin/stdout。
  2. 75 |
  3. 将核心脚本发送到远程执行,在importer上挂钩子,在stdout上挂钩子。
  4. 76 |
  5. 在本地和远程交互,发送指令要求执行。
  6. 77 |
78 | 79 |
80 |
81 |
82 |
83 |
84 |

更多细节

85 |

全是ssh家族的。

86 |
    87 |
  • 支持自定义远程启动方法。 88 |
      89 |
    • ssh子进程。
    • 90 |
    • 远程可以sudo(这是自然)来获得root权限。
    • 91 |
    • paramiko。
    • 92 |
  • 93 |
  • 支持自定义通讯协议。 94 |
      95 |
    • raw data。marshal + zlib。size leaded frame。
    • 96 |
    • base64 coded。marshal + zlib + base64。 97 | 支持穿越一些比较特殊的过滤设备。
    • 98 |
  • 99 |
100 | 101 |
102 |
103 | 104 |

实践中使用base64 coded模式,加上特殊的跳板机启动方案。

105 |

在七牛的集群上执行任意代码。主要用来检查远程的漏洞修补情况。

106 | 107 |
108 |
109 |
110 |
111 |
112 |
113 |

还有什么

114 |

发挥你的想象力。

115 |

原则上,可以用pexpect启动一个ssh来获得远程管道。

116 |

连接堡垒机,模拟输入,登录到目标机器。

117 |

再模拟手工执行python的过程,将stdin/stdout转交给rir。

118 |

一切就像真人一样。

119 | 120 |
121 |
122 | 123 |

在远程执行任意代码,基本不会触发关键字。

124 |

如果不想被解出来,我们还有。。。

125 |

diffie-hellman key exchange algorithm + aes-256-cfb embed.

126 |

of cause, you will got fired.

127 |

but they have no evidence about what you did...

128 | 129 |
130 |
131 |
132 |
133 |
134 |

rir可以做到什么

135 |
    136 |
  1. 在不同构的机器上执行运维脚本。 137 | (脚本需要是python,且在多个机器上都兼容)
  2. 138 |
  3. 免去部署过程,测试服务端/客户端s的执行。
  4. 139 |
  5. **和***也是很好用的,你懂。
  6. 140 |
141 | 142 |
143 |
144 |
145 |
146 |

根本上RIR是一个远程执行框架

147 |

你可以拿去自high

148 | 149 |
150 |
151 |
152 |
153 |

效率呢?

154 |

不算太高。在国内的用例里,每个的执行时间都到了秒级。

155 |

将一个web项目发到远程执行甚至用了2s。

156 |

所以,如果有大量的设备(例如上千),还是老老实实用salt比较实在。

157 |

但是对一次性应用来说,几秒的时间完全不痛不痒。

158 | 159 |
160 |
161 |
162 |
163 |

内附脚本

164 |
    165 |
  • hwinfo: 收集远程机器的各种数据。
  • 166 |
  • sync: 同步远程文件。
  • 167 |
168 | 169 |
170 |
171 |
172 |
173 |

sync?

174 |

没错。sync又是一个非常复杂的小系统。

175 |
    176 |
  • 使用yaml作为配置和数据存储。
  • 177 |
  • 定义远程的一组特定目录。
  • 178 |
  • 从远程同步文件内容和属性/属主到本地。
  • 179 |
  • 只同步需要更新的,相同内容不需要通过网络。
  • 180 |
  • 从本地同步文件内容和属性/属主到远程。
  • 181 |
182 | 183 |
184 |
185 |
186 |
187 |

所以呢?sync是做什么的?

188 |

例如你有20台机器,家里有几台,自己有几台,帮朋友管几台。

189 |

每个系统都不一样,配置各自不一样。你打算如何管理配置?

190 |

sync可以将远程配置和属性批量拉到本地,再提交到git里。

191 |

支持多人对同样的机器组进行协作更新。merge配置,pull,push。。。

192 |

你们懂。

193 |

反正push到机器上的时候不要一起来就行。

194 | 195 |
196 |
197 |
198 |
199 |

sync live show

200 | 201 |
202 |
203 |
204 |
205 |

效率呢?

206 |

7台内网机器+2台国内物理机。

207 |

1.5s。

208 |

其实一台机器同步一些配置文件下来并不需要多久。 209 | 只要你的机器能并发执行。。。

210 | 211 |
212 |
213 |
214 |
215 |

License

216 |

Run It Remote: BSD-3-clause 217 | This slide: cc-by-sa3.0

218 |

Powered by MDP 219 | GPLv3

220 | 221 |
222 |
223 |
224 |
225 |

Q&A

226 | 227 |
228 |
229 |
230 | 231 | 232 | 258 | 259 | 260 | -------------------------------------------------------------------------------- /rir/pycon.md: -------------------------------------------------------------------------------- 1 | # run it remote 2 | 3 | # 做什么用的 4 | 5 | 用将代码放到远程执行。大概就是这样子: 6 | 7 | scp script.py host: 8 | ssh host python script.py 9 | 10 | # 为什么要用rir 11 | 12 | 脚本的远程执行往往伴随几个困难。 13 | 14 | 1. 目标机器在很深的内网,需要scp后再scp。 15 | 2. 更新脚本内容后需要重新copy。 16 | 3. 一次启动多个节点形成集群,更新代码后需要全部关闭再全部启动。 17 | 4. 没有api接口。 18 | 5. 一样工作,重复劳动。 19 | 20 | # rir live show 21 | 22 | # rir做了什么 23 | 24 | * 本地可以将任意函数放到远程执行。 25 | * 支持远程import服务端上的库。 26 | * 无缝支持py/pyc。 27 | * 只有py的情况下不需要在远程写文件,只有进程,不触发任何警报。 28 | * so需要保证本地文件在远程可以导入(架构和依赖OK)。 29 | * 支持远程打开本地文件,本地stdout/stderr。远程logging打印回本地。 30 | * 支持远程回调本地函数。 31 | 32 | # 怎么做的 33 | 34 | 1. ssh host python -c '...',在远程执行一个引导代码。 35 | 获得stdin/stdout。 36 | 2. 将核心脚本发送到远程执行,在importer上挂钩子,在stdout上挂钩子。 37 | 3. 在本地和远程交互,发送指令要求执行。 38 | 39 | # 更多细节 40 | 41 | 全是ssh家族的。 42 | 43 | * 支持自定义远程启动方法。 44 | * ssh子进程。 45 | * 远程可以sudo(这是自然)来获得root权限。 46 | * paramiko。 47 | * 支持自定义通讯协议。 48 | * raw data。marshal + zlib。size leaded frame。 49 | * base64 coded。marshal + zlib + base64。 50 | 支持穿越一些比较特殊的过滤设备。 51 | 52 | --- 53 | 54 | 实践中使用base64 coded模式,加上特殊的跳板机启动方案。 55 | 56 | 在七牛的集群上执行任意代码。主要用来检查远程的漏洞修补情况。 57 | 58 | # 还有什么 59 | 60 | 发挥你的想象力。 61 | 62 | 原则上,可以用pexpect启动一个ssh来获得远程管道。 63 | 64 | 连接堡垒机,模拟输入,登录到目标机器。 65 | 66 | 再模拟手工执行python的过程,将stdin/stdout转交给rir。 67 | 68 | 一切就像真人一样。 69 | 70 | --- 71 | 72 | 在远程执行任意代码,基本不会触发关键字。 73 | 74 | 如果不想被解出来,我们还有。。。 75 | 76 | diffie-hellman key exchange algorithm + aes-256-cfb embed. 77 | 78 | of cause, you will got fired. 79 | 80 | but they have no evidence about what you did... 81 | 82 | # rir可以做到什么 83 | 84 | 1. 在不同构的机器上执行运维脚本。 85 | (脚本需要是python,且在多个机器上都兼容) 86 | 2. 免去部署过程,测试服务端/客户端s的执行。 87 | 3. \*\*和\*\*\*也是很好用的,你懂。 88 | 89 | # 根本上RIR是一个远程执行框架 90 | 91 | 你可以拿去自high 92 | 93 | # 效率呢? 94 | 95 | 不算太高。在国内的用例里,每个的执行时间都到了秒级。 96 | 97 | 将一个web项目发到远程执行甚至用了2s。 98 | 99 | 所以,如果有大量的设备(例如上千),还是老老实实用salt比较实在。 100 | 101 | 但是对一次性应用来说,几秒的时间完全不痛不痒。 102 | 103 | # 内附脚本 104 | 105 | * hwinfo: 收集远程机器的各种数据。 106 | * sync: 同步远程文件。 107 | 108 | # sync? 109 | 110 | 没错。sync又是一个非常复杂的小系统。 111 | 112 | * 使用yaml作为配置和数据存储。 113 | * 定义远程的一组特定目录。 114 | * 从远程同步文件内容和属性/属主到本地。 115 | * 只同步需要更新的,相同内容不需要通过网络。 116 | * 从本地同步文件内容和属性/属主到远程。 117 | 118 | # 所以呢?sync是做什么的? 119 | 120 | 例如你有20台机器,家里有几台,自己有几台,帮朋友管几台。 121 | 122 | 每个系统都不一样,配置各自不一样。你打算如何管理配置? 123 | 124 | sync可以将远程配置和属性批量拉到本地,再提交到git里。 125 | 126 | 支持多人对同样的机器组进行协作更新。merge配置,pull,push。。。 127 | 128 | 你们懂。 129 | 130 | 反正push到机器上的时候不要一起来就行。 131 | 132 | # sync live show 133 | 134 | # 效率呢? 135 | 136 | 7台内网机器+2台国内物理机。 137 | 138 | 1.5s。 139 | 140 | 其实一台机器同步一些配置文件下来并不需要多久。 141 | 只要你的机器能并发执行。。。 142 | 143 | # License 144 | 145 | Run It Remote: BSD-3-clause 146 | This slide: cc-by-sa3.0 147 | 148 | Powered by [MDP](https://github.com/visit1985/mdp) 149 | GPLv3 150 | 151 | # Q&A 152 | -------------------------------------------------------------------------------- /rir/sync.rec: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shell909090/slides/d8aa123521daedee78e85e06f80c621929afaebd/rir/sync.rec --------------------------------------------------------------------------------