├── .gitignore ├── Makefile ├── algorithm ├── arithmetic │ ├── exponential-backoff.rst │ ├── image │ │ └── power-law.png │ ├── index.rst │ ├── parse-string-to-number.rst │ └── power-law.rst └── taocp │ ├── chp1.rst │ └── index.rst ├── conf.py ├── fixed_width_pyramid ├── disqus.html ├── layout.html ├── static │ ├── dialog-note.png │ ├── dialog-seealso.png │ ├── dialog-todo.png │ ├── dialog-topic.png │ ├── dialog-warning.png │ ├── epub.css │ ├── footerbg.png │ ├── headerbg.png │ ├── ie6.css │ ├── middlebg.png │ ├── pyramid.css_t │ └── transparent.gif └── theme.conf ├── index.rst ├── introspection └── zeng-guofan-home-letter │ └── index.rst ├── language ├── clojure │ ├── basic_type.rst │ ├── composite_type.rst │ ├── concurrency-and-parallelism.rst │ ├── condition.rst │ ├── function.rst │ ├── index.rst │ ├── misc.rst │ ├── multimethod.rst │ ├── namespace.rst │ ├── protocol.rst │ ├── record.rst │ ├── reference-type.rst │ ├── seq.rst │ └── variable.rst └── mruby-code-analysis │ ├── index.rst │ └── string.rst ├── make.bat ├── network ├── distrubuted_systems_concepts_and_design │ ├── chp18.rst │ └── index.rst ├── posav4 │ ├── chp11.rst │ ├── image │ │ └── reactor.png │ └── index.rst └── unp │ ├── chp1.rst │ ├── chp2.rst │ ├── chp3.rst │ ├── chp4.rst │ ├── code │ ├── chp1 │ │ ├── daytimetcpcli.c │ │ └── daytimetcpsrv.c │ ├── chp3 │ │ └── ip_echo.c │ └── chp4 │ │ ├── cli │ │ ├── client │ │ ├── server │ │ ├── test_getsockname_getpeername_client.c │ │ └── test_getsockname_getpeername_server.c │ ├── image │ ├── protocol.dot │ └── protocol.png │ └── index.rst ├── os ├── apue │ ├── chp10.rst │ ├── chp11.rst │ ├── chp14.rst │ ├── chp3.rst │ ├── chp4.rst │ ├── chp8.rst │ ├── code │ │ ├── 10-signal.c │ │ ├── 11-1.c │ │ ├── 11-2.c │ │ ├── 14-1.c │ │ ├── 3-close.c │ │ ├── 3-creat.c │ │ ├── 3-dup.c │ │ ├── 3-hole.c │ │ ├── 3-lseek.c │ │ ├── 3-open-create-file.c │ │ ├── 3-open-create-with-excl.c │ │ ├── 3-pread.c │ │ ├── 3-print-file-descriptor.c │ │ ├── 3-read.c │ │ ├── 3-seek-data-and-hole.c │ │ ├── 3-show-hole.c │ │ ├── 3-write.c │ │ ├── 4-change-owner.c │ │ ├── 4-owner.c │ │ ├── 4-permission.c │ │ ├── 4-print-size.c │ │ ├── 4-print-type-2.c │ │ ├── 4-print-type.c │ │ ├── 4-set-owner-x-permission.c │ │ ├── 4-special-permission.c │ │ ├── 4-truncate.c │ │ ├── 8-data-duplicate.c │ │ ├── 8-exec.c │ │ ├── 8-fork.c │ │ ├── 8-get-exit-status.c │ │ ├── 8-getpid.c │ │ ├── 8-ids.c │ │ ├── 8-parent-wait-for-child.c │ │ ├── 8-share-file-text │ │ ├── 8-share-file.c │ │ ├── 8-vfork.c │ │ ├── hello_world.c │ │ ├── test-3-close │ │ ├── test-3-creat │ │ ├── test-3-hole │ │ ├── test-3-lseek │ │ ├── test-3-open-create-file.txt │ │ ├── test-3-open-create-with-excl.txt │ │ ├── test-3-print-file-descriptor │ │ ├── test-3-read │ │ ├── test-3-write │ │ └── test-4-truncate │ ├── image │ │ ├── 4-12.png │ │ ├── 8-share-file.png │ │ ├── share-file-1.png │ │ ├── share-file-2.png │ │ └── share-file-3.png │ ├── index.rst │ └── use-code.rst └── fedora │ ├── index.rst │ └── yum.rst ├── storage ├── mariadb │ ├── index.rst │ ├── install.rst │ ├── primary-db-operation.rst │ ├── primary-query-operation.rst │ ├── primary-table-operation.rst │ ├── send-query.rst │ └── version.rst ├── redis_code_analysis │ ├── adlist.rst │ ├── adlist │ │ ├── list_and_list_node.dot │ │ └── list_and_list_node.png │ ├── cluster.rst │ ├── cluster │ │ ├── cluster.c │ │ └── cluster.h │ ├── dict.rst │ ├── dict │ │ ├── add-element.png │ │ ├── create-dict.png │ │ ├── incremental-rehashing-functions.png │ │ └── relationship.png │ ├── event-driven.rst │ ├── event-driven │ │ ├── event-loop.dot │ │ └── event-loop.png │ ├── index.rst │ ├── keyspace-notification.rst │ ├── lua-scripting.rst │ ├── pubsub.rst │ ├── pubsub │ │ ├── list_and_list_node.dot │ │ ├── list_and_list_node.png │ │ ├── psubscribe.dot │ │ ├── psubscribe.png │ │ ├── pubsub.dot │ │ ├── pubsub.png │ │ ├── subscribe.dot │ │ └── subscribe.png │ ├── pubsub_command.rst │ ├── replication.rst │ ├── replication │ │ └── replication.c │ ├── sds.rst │ ├── sds │ │ ├── sdshdr.dot │ │ └── sdshdr.png │ ├── sentinel.rst │ ├── sentinel │ │ └── sentinel.c │ ├── transaction.rst │ └── watch-and-unwatch.rst └── reuse-redis-module │ ├── adlist.rst │ ├── code │ ├── adlist │ │ ├── adlist.c │ │ ├── adlist.h │ │ ├── main.c │ │ ├── object.c │ │ ├── object.h │ │ ├── zmalloc.c │ │ └── zmalloc.h │ ├── dict │ │ ├── dict.c │ │ ├── dict.h │ │ ├── fmacros.h │ │ ├── int_dict_type.c │ │ ├── int_dict_type.h │ │ ├── main.c │ │ ├── zmalloc.c │ │ └── zmalloc.h │ ├── intset │ │ ├── endianconv.c │ │ ├── endianconv.h │ │ ├── intset.c │ │ ├── intset.h │ │ ├── main.c │ │ ├── zmalloc.c │ │ └── zmalloc.h │ ├── sds │ │ ├── main.c │ │ ├── sds.c │ │ ├── sds.h │ │ ├── zmalloc.c │ │ └── zmalloc.h │ └── ziplist │ │ ├── endianconv.c │ │ ├── endianconv.h │ │ ├── fmacros.h │ │ ├── main.c │ │ ├── util.c │ │ ├── util.h │ │ ├── ziplist.c │ │ ├── ziplist.h │ │ ├── zmalloc.c │ │ └── zmalloc.h │ ├── dict.rst │ ├── index.rst │ ├── intset.rst │ ├── sds.rst │ └── ziplist.rst ├── system ├── csapp │ ├── chp1.rst │ ├── image │ │ ├── 1.10.png │ │ ├── 1.11.png │ │ ├── 1.13.png │ │ ├── 1.14.png │ │ ├── 1.17.png │ │ ├── 1.18.png │ │ ├── 1.4.png │ │ ├── 1.5.png │ │ ├── 1.6.png │ │ ├── 1.7.png │ │ ├── 1.8.png │ │ ├── 1.9.png │ │ ├── compilation_system.png │ │ └── hello_c_in_ascii.png │ └── index.rst └── on_building_systems_that_will_fail.rst └── thinking_and_learning ├── bakuman ├── chp20 │ ├── 1.png │ ├── 2.png │ ├── 3.png │ ├── 4.png │ ├── 5.png │ ├── 6.png │ ├── 7.png │ └── 8.png └── index.rst └── pragmatic_thinking_and_learning ├── chp2.rst ├── chp3.rst ├── image ├── dreyfus-model-of-skill-acquisition.png └── idea.png └── index.rst /.gitignore: -------------------------------------------------------------------------------- 1 | _build/ 2 | *.swp 3 | *.out 4 | *.o 5 | *.pyc 6 | .DS_Store 7 | -------------------------------------------------------------------------------- /algorithm/arithmetic/exponential-backoff.rst: -------------------------------------------------------------------------------- 1 | 指数补偿 —— Exponential backoff 2 | =================================== 3 | 4 | 指数补偿指的是,在执行事件时,通过反馈,逐渐降低某个过程的速率,从而最终找到一个合适的速率(来处理事件)。 5 | 6 | 指数补偿通常用于网络和传输协议,比如在进行网络连接时,如果第一次请求失败,那么可以等待 :math:`t_1` 之后重试,如果再次请求还是失败,那么等待 :math:`t_2` 之后重试。。。 7 | 8 | 重试可以一直继续下去,或者等待次数或等待时间超过特定值为止。 9 | 10 | 等待的时间 :math:`t_n` 可以是随机选择,也可以随着重试的次数而逐渐加大,诸如此类。 11 | 12 | 详细内容请参考维基百科的 `Exponential backoff `_ 词条。 13 | 14 | 应用 15 | ------- 16 | 17 | 《UNIX 环境高级编程,第二版》(APUE,2E) 16.4 节提供了一个带重试的 ``socket`` 连接程序, 18 | 如果连接失败, 19 | 那么程序就睡眠一段时间再尝试, 20 | 每失败一次睡眠的时间就延长一些: 21 | 22 | .. code-block:: c 23 | 24 | #include "apue.h" 25 | #include 26 | 27 | #define MAXSLEEP 128 28 | 29 | int 30 | connect_retry(int sockfd, const struct sockaddr *addr, socklen_t alen) 31 | { 32 | int nsec; 33 | 34 | for (nsec = 1; nsec < MAXSLEEP; nsec <<=1) { 35 | if (connect(sockfd, addr, alen) == 0) { 36 | // connect accepted. 37 | return 0; 38 | } 39 | if (nsec <= MAXSLEEP/2) 40 | sleep(nsec); 41 | } 42 | 43 | return -1; 44 | } 45 | 46 | -------------------------------------------------------------------------------- /algorithm/arithmetic/image/power-law.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huangzworks/note/82ce594784a1f31095fc7a40cc0ab11e863c5db4/algorithm/arithmetic/image/power-law.png -------------------------------------------------------------------------------- /algorithm/arithmetic/index.rst: -------------------------------------------------------------------------------- 1 | 算术 2 | ========= 3 | 4 | .. toctree:: 5 | :maxdepth: 1 6 | 7 | power-law 8 | exponential-backoff 9 | parse-string-to-number 10 | -------------------------------------------------------------------------------- /algorithm/arithmetic/parse-string-to-number.rst: -------------------------------------------------------------------------------- 1 | 将字符串解释为数字 —— Parse string to number 2 | ================================================ 3 | 4 | 解释带符号整数 5 | ---------------------------- 6 | 7 | TODO: 补充介绍和所使用的算法。 8 | 9 | .. 来自 redis 源码中 util.c 文件的 string2ll 函数 10 | 11 | .. code-block:: python 12 | 13 | def parse_int(s): 14 | 15 | # handle negative number 16 | if s[0] == '-': 17 | negative = True 18 | s = s[1:] 19 | else: 20 | negative = False 21 | 22 | result = 0 23 | 24 | for c in s: 25 | if result != 0: result *= 10 26 | result += int(c) 27 | 28 | if negative: 29 | return -result 30 | else: 31 | return result 32 | 33 | 34 | if __name__ == "__main__": 35 | 36 | # zero 37 | assert parse_int(str(0)) == 0 38 | 39 | # non-zero and non-negative 40 | assert parse_int(str(123)) == 123 41 | 42 | # negative 43 | assert parse_int(str(-123)) == -123 44 | 45 | -------------------------------------------------------------------------------- /algorithm/arithmetic/power-law.rst: -------------------------------------------------------------------------------- 1 | 幂次定律 —— Power Law 2 | ========================== 3 | 4 | 如果某件事的发生频率和它的某个属性成幂关系,那么这个频率就可以称之为符合幂次定律。 5 | 6 | 幂次定律的表现是, 7 | *少数几个事件的发生频率占了整个发生频率的大部分, 而其余的大多数事件只占整个发生频率的一个小部分*\ , 8 | 如图: 9 | 10 | .. figure:: image/power-law.png 11 | 12 | 右边黄色为长尾,左边绿色为占主宰地位的少数事件。(图片来源于维基百科) 13 | 14 | 详细信息请参考维基百科的 `Power Law 词条 `_ 。 15 | 16 | MMDays 博客的\ `《淺談網路世界的Power Law現象》 `_\ 一系列文章也是很好的资料。 17 | 18 | 应用示例 19 | --------- 20 | 21 | 在 Redis 的 ``t_zset.c`` 模块中,跳跃表层数的随机值就由符合幂次定律的函数定义, 22 | 数值越大,函数生成它的几率就越小: 23 | 24 | .. code-block:: c 25 | 26 | /* Returns a random level for the new skiplist node we are going to create. 27 | * The return value of this function is between 1 and ZSKIPLIST_MAXLEVEL 28 | * (both inclusive), with a powerlaw-alike distribution where higher 29 | * levels are less likely to be returned. 30 | * 31 | * 返回一个介于 1 和 ZSKIPLIST_MAXLEVEL 之间的随机值,作为节点的层数。 32 | */ 33 | #define ZSKIPLIST_P 0.25 34 | #define ZSKIPLIST_MAXLEVEL 32 35 | int zslRandomLevel(void) { 36 | int level = 1; 37 | while ((random()&0xFFFF) < (ZSKIPLIST_P * 0xFFFF)) 38 | level += 1; 39 | return (level 2 | 3 |

4 | 留言 5 | 6 |

7 | 8 |
9 | 20 | 21 | comments powered by Disqus 22 | 23 | 24 | -------------------------------------------------------------------------------- /fixed_width_pyramid/static/dialog-note.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huangzworks/note/82ce594784a1f31095fc7a40cc0ab11e863c5db4/fixed_width_pyramid/static/dialog-note.png -------------------------------------------------------------------------------- /fixed_width_pyramid/static/dialog-seealso.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huangzworks/note/82ce594784a1f31095fc7a40cc0ab11e863c5db4/fixed_width_pyramid/static/dialog-seealso.png -------------------------------------------------------------------------------- /fixed_width_pyramid/static/dialog-todo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huangzworks/note/82ce594784a1f31095fc7a40cc0ab11e863c5db4/fixed_width_pyramid/static/dialog-todo.png -------------------------------------------------------------------------------- /fixed_width_pyramid/static/dialog-topic.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huangzworks/note/82ce594784a1f31095fc7a40cc0ab11e863c5db4/fixed_width_pyramid/static/dialog-topic.png -------------------------------------------------------------------------------- /fixed_width_pyramid/static/dialog-warning.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huangzworks/note/82ce594784a1f31095fc7a40cc0ab11e863c5db4/fixed_width_pyramid/static/dialog-warning.png -------------------------------------------------------------------------------- /fixed_width_pyramid/static/footerbg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huangzworks/note/82ce594784a1f31095fc7a40cc0ab11e863c5db4/fixed_width_pyramid/static/footerbg.png -------------------------------------------------------------------------------- /fixed_width_pyramid/static/headerbg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huangzworks/note/82ce594784a1f31095fc7a40cc0ab11e863c5db4/fixed_width_pyramid/static/headerbg.png -------------------------------------------------------------------------------- /fixed_width_pyramid/static/ie6.css: -------------------------------------------------------------------------------- 1 | * html img, 2 | * html .png{position:relative;behavior:expression((this.runtimeStyle.behavior="none")&&(this.pngSet?this.pngSet=true:(this.nodeName == "IMG" && this.src.toLowerCase().indexOf('.png')>-1?(this.runtimeStyle.backgroundImage = "none", 3 | this.runtimeStyle.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='" + this.src + "',sizingMethod='image')", 4 | this.src = "_static/transparent.gif"):(this.origBg = this.origBg? this.origBg :this.currentStyle.backgroundImage.toString().replace('url("','').replace('")',''), 5 | this.runtimeStyle.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='" + this.origBg + "',sizingMethod='crop')", 6 | this.runtimeStyle.backgroundImage = "none")),this.pngSet=true) 7 | );} 8 | -------------------------------------------------------------------------------- /fixed_width_pyramid/static/middlebg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huangzworks/note/82ce594784a1f31095fc7a40cc0ab11e863c5db4/fixed_width_pyramid/static/middlebg.png -------------------------------------------------------------------------------- /fixed_width_pyramid/static/transparent.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huangzworks/note/82ce594784a1f31095fc7a40cc0ab11e863c5db4/fixed_width_pyramid/static/transparent.gif -------------------------------------------------------------------------------- /fixed_width_pyramid/theme.conf: -------------------------------------------------------------------------------- 1 | [theme] 2 | inherit = basic 3 | stylesheet = pyramid.css 4 | pygments_style = sphinx.pygments_styles.PyramidStyle 5 | -------------------------------------------------------------------------------- /index.rst: -------------------------------------------------------------------------------- 1 | .. Right Track Wrong Train documentation master file, created by 2 | sphinx-quickstart on Sat Oct 6 22:07:15 2012. 3 | You can adapt this file completely to your liking, but it should at least 4 | contain the root `toctree` directive. 5 | 6 | huangz/note 7 | ============================ 8 | 9 | 这里是 `huangz `_ 的在线笔记网站, 10 | 追踪更新请关注\ `这个 GitHub 项目 `_\ , 11 | 目前不支持 RSS 订阅。 12 | 13 | 14 | .. _language: 15 | 16 | 语言 17 | -------- 18 | 19 | .. toctree:: 20 | :maxdepth: 2 21 | 22 | language/clojure/index 23 | language/mruby-code-analysis/index 24 | 25 | 26 | .. _storage: 27 | 28 | 存储 29 | --------- 30 | 31 | .. toctree:: 32 | :maxdepth: 2 33 | 34 | storage/redis_code_analysis/index 35 | storage/reuse-redis-module/index 36 | storage/mariadb/index 37 | 38 | 39 | .. _os: 40 | 41 | 操作系统 42 | ------------ 43 | 44 | .. toctree:: 45 | :maxdepth: 2 46 | 47 | os/apue/index 48 | os/fedora/index 49 | 50 | 51 | .. _network: 52 | 53 | 网络 54 | -------- 55 | 56 | .. toctree:: 57 | :maxdepth: 2 58 | 59 | network/unp/index 60 | network/distrubuted_systems_concepts_and_design/index 61 | network/posav4/index 62 | 63 | 64 | .. _algorithm: 65 | 66 | 算法 67 | --------- 68 | 69 | .. toctree:: 70 | :maxdepth: 2 71 | 72 | algorithm/arithmetic/index 73 | 74 | 75 | .. _system: 76 | 77 | 系统 78 | ------ 79 | 80 | .. toctree:: 81 | :maxdepth: 2 82 | 83 | system/on_building_systems_that_will_fail 84 | system/csapp/index 85 | 86 | .. _thinking_and_learning: 87 | 88 | 思考与学习 89 | -------------- 90 | 91 | .. toctree:: 92 | :maxdepth: 2 93 | 94 | thinking_and_learning/pragmatic_thinking_and_learning/index 95 | thinking_and_learning/bakuman/index 96 | 97 | 98 | 自省 99 | ------------ 100 | 101 | .. toctree:: 102 | :maxdepth: 2 103 | 104 | introspection/zeng-guofan-home-letter/index 105 | -------------------------------------------------------------------------------- /introspection/zeng-guofan-home-letter/index.rst: -------------------------------------------------------------------------------- 1 | 《曾国藩家书》笔记 2 | ======================== 3 | 4 | 咸丰四年九月十三日:勿使子侄骄奢淫佚 5 | --------------------------------------- 6 | 7 | 功名之地,自古难居,兄以在籍之官,募勇造船,成此一番事业,名震一时,人之好名,谁不如我? 8 | 我有美名,则人必有受不美之名者,相形之际,盖难为情。 9 | 兄惟谨慎谦虚,时时省惕而已,……? 10 | 11 | 12 | 吾在外,既有权势,则家中子侄,最易流于骄,流于佚。 13 | 二字皆败家之道也,万望诸弟刻刻留心,勿使后辈近于此二字,至要至要。 14 | 15 | 16 | 咸丰五年八月二十七日早:居家之道不可有余财 17 | ----------------------------------------------- 18 | 19 | 生当乱世,居家之道,不可有余财,多财则终为患害。 20 | 又不可过于安逸懒惰。 21 | …… 22 | 仕宦之家,不蓄积银钱,使子弟自觉一无可恃,一日不勤,则将有饥寒之患,则子弟渐渐勤劳,知谋所以自立矣。 23 | 24 | 25 | 咸丰六年十一月二十九日:得两弟为帮手 26 | ---------------------------------------- 27 | 28 | 纪泽看《\ `汉书 `_\ 》,需以勤敏行之,每日至少亦须看二十页。 29 | 不必惑于在精不在多之说。 30 | 今日半页,明日数页,又明日耽搁间断,或数年而不能毕一部。 31 | 如煮饭然,熄火则冷,小火则不熟,须用大柴大火,乃易成也。 32 | 甲五经书已读毕否? 33 | 须速点速读,不必一一求熟,恐因求熟之一字,而终身未能读完经书。 34 | 吾乡子弟,未读完经书者甚多,此后当力戒之。 35 | 36 | 咸丰十一年正月初四日:教子弟去骄气惰习 37 | ------------------------------------------ 38 | 39 | 天地间唯谦谨是载福之道。 40 | 骄则满,满则\ `倾 `_\ 矣。 41 | 凡动口动笔, 42 | 厌人之\ `俗 `_\ , 43 | 嫌人之\ `鄙 `_\ , 44 | 议人之短, 45 | 发人之覆, 46 | 皆骄也。 47 | 48 | 咸丰十一年二月二十四日:教子弟牢记祖训八字 49 | ------------------------------------------------ 50 | 51 | 余日记册中,又有八本之说,曰读书以训诂为本,作诗文以声调为本,事亲以得欢心为本,养身以戒怒为本,立身以不妄语为本,居家以不晏起为本,做官以不要钱为本,行军以不扰民为本。 52 | -------------------------------------------------------------------------------- /language/clojure/basic_type.rst: -------------------------------------------------------------------------------- 1 | .. highlight:: clojure 2 | 3 | 基本类型 4 | ========== 5 | 6 | 整数 7 | ----- 8 | 9 | 只要有需要的话,整数会在多个类型之间自动转换,以满足长度需求: 10 | 11 | :: 12 | 13 | user=> (class 10086) 14 | java.lang.Long 15 | 16 | user=> (class 10086000000000000000000000000000000) 17 | clojure.lang.BigInt 18 | 19 | 20 | 有限精度浮点数 21 | ---------------- 22 | 23 | Clojure 默认使用有限精度的浮点数,类型为 ``Double`` : 24 | 25 | :: 26 | 27 | user=> (class 3.14) 28 | java.lang.Double 29 | 30 | 31 | 无限精度浮点数 32 | ---------------- 33 | 34 | 无限精度浮点数需要在数值的最后加上后缀 ``M`` : 35 | 36 | :: 37 | 38 | user=> (class 3.144444444444444444444444444444444444444444444444444444444444444444444444M) 39 | java.math.BigDecimal 40 | 41 | 无限精度浮点数的类型为 ``BigDecimal`` 。 42 | 43 | 44 | 有理数 45 | ------- 46 | 47 | 为了进一步加强对高精度计算的支持, Clojure 提供了有理数类型: 48 | 49 | :: 50 | 51 | user=> (class (/ 1 3)) 52 | clojure.lang.Ratio 53 | 54 | 在进行高精度计算时,使用有理数会比使用无限精度浮点数得到更精确的结果。 55 | 56 | 57 | 字符 58 | ----- 59 | 60 | 单个字符用 ``\`` 作为前缀来表示: 61 | 62 | :: 63 | 64 | user=> \a 65 | \a 66 | 67 | user=> (class \a) 68 | java.lang.Character 69 | 70 | 71 | 字符串 72 | ------- 73 | 74 | 字符串使用 ``"`` 符号包围: 75 | 76 | :: 77 | 78 | user=> "i'm a string" 79 | "i'm a string" 80 | 81 | user=> (class "i'm a string") 82 | java.lang.String 83 | 84 | 85 | 关键字 86 | ------ 87 | 88 | 一个关键字总是指向它自身,因为这个原因,它可以用作是比字符串更高效的比对手段: 89 | 90 | :: 91 | 92 | user=> :keyword 93 | :keyword 94 | 95 | user=> (class :keyword) 96 | clojure.lang.Keyword 97 | 98 | user=> (identical? :keyword :keyword) ; 对象比对 99 | true 100 | 101 | user=> (= :keyword :keyword) ; 值比对 102 | true 103 | 104 | 105 | 符号 106 | ----- 107 | 108 | 符号在求值时,返回的是它在当前上下文中所指向的值: 109 | 110 | :: 111 | 112 | user=> (def number 10086) ; 定义一个变量 113 | #'user/number 114 | 115 | user=> 'number ; 一个符号值 116 | number 117 | 118 | user=> (eval 'number) ; 求值符号值 119 | 10086 120 | 121 | 122 | nil 123 | ------ 124 | 125 | ``nil`` 是一个特殊值,它表示“空”,如果一个函数不返回任何值,那么它返回 ``nil`` 。 126 | 127 | :: 128 | 129 | user=> nil 130 | nil 131 | 132 | user=> (class nil) 133 | nil 134 | 135 | user=> (prn) 136 | 137 | nil 138 | 139 | 140 | 布尔 141 | ------ 142 | 143 | 布尔值包含 ``true`` 和 ``false`` 两个值: 144 | 145 | :: 146 | 147 | user=> true 148 | true 149 | 150 | user=> false 151 | false 152 | 153 | user=> (class true) 154 | java.lang.Boolean 155 | 156 | user=> (class false) 157 | java.lang.Boolean 158 | 159 | 在 Clojure 中,除了 ``nil`` 和 ``false`` 之外,其他任何值都被看作是 ``true`` ,这和 C 语言和其他一些函数式语言稍有不同,需要注意: 160 | 161 | :: 162 | 163 | user=> (false? nil) 164 | false 165 | 166 | user=> (false? false) 167 | true 168 | 169 | user=> (false? 0) 170 | false 171 | 172 | user=> (false? "") 173 | false 174 | 175 | user=> (false? (list)) 176 | false 177 | 178 | 179 | 函数 180 | ----- 181 | 182 | 函数也是 Clojure 中的第一类对象,因此它也是其中一种基本类型值: 183 | 184 | :: 185 | 186 | user=> (fn [] "hello moto") 187 | # 188 | 189 | 稍后的章节会介绍更多和函数相关的内容。 190 | 191 | 192 | 元数据 193 | ------- 194 | 195 | 元数据(metadata)是一个映射,可以添加到一个符号或者一个 collection 当中,用作编译提示,或者给开发者提供额外的信息。 196 | 197 | ``with-meta`` 用于为对象添加元数据: 198 | 199 | :: 200 | 201 | user=> (def s (with-meta 'a-symbol {:author "huangz"})) 202 | #'user/s 203 | 204 | user=> s 205 | a-symbol 206 | 207 | 以上代码为符号 ``s`` 添加了包含 ``:author`` 信息的元数据。 208 | 209 | ``meta`` 函数用于取出对象的元数据: 210 | 211 | :: 212 | 213 | user=> (meta s) 214 | {:author "huangz"} 215 | 216 | 217 | 正则表达式 218 | ------------ 219 | 220 | 正则表达式可以通过 reader 宏 ``#"pattern"`` 在读入期创建,或者使用 ``re-pattern`` 函数在运行时创建,这两种方式都会创建一个 ``java.util.regexp.Pattern`` 对象。 221 | 222 | :: 223 | 224 | user=> (class #"pattern") 225 | java.util.regex.Pattern 226 | 227 | user=> (re-seq #"[0-9]+" "abs123def345ghi567") 228 | ("123" "345" "567") 229 | 230 | user=> (re-seq #"\w+" "one-two/three") 231 | ("one" "two" "three") 232 | 233 | ``re-seq`` 函数惰性地返回字符串中的所有匹配。 234 | -------------------------------------------------------------------------------- /language/clojure/concurrency-and-parallelism.rst: -------------------------------------------------------------------------------- 1 | .. highlight:: clojure 2 | 3 | 并发与并行 4 | ============= 5 | 6 | 7 | 延时计算 8 | ------------- 9 | 10 | 使用 ``delay`` 包裹一个给定的表达式,并产生一个延时对象。 11 | 12 | 当使用 ``deref`` 或者 ``@`` 对这个延时对象进行解引用时,被包裹的表达式才会被求值: 13 | 14 | :: 15 | 16 | user=> (def d (delay (println "Running...") :done!)) 17 | #'user/d 18 | 19 | user=> d ; 未求值的 delay 20 | # 21 | 22 | user=> @d ; 解引用,等同于调用 (deref d) 23 | Running... 24 | :done! 25 | 26 | user=> @d ; 值被求值一次之后就会被缓存 27 | :done! 28 | 29 | 因为并不对被包裹的表达式进行求值,所以对 ``delay`` 语句的调用总是立即被返回。 30 | 31 | 使用 ``realized?`` 可以检查一个 ``delay`` 是否已经被解引用过: 32 | 33 | :: 34 | 35 | user=> (def dd (delay :done!)) 36 | #'user/dd 37 | 38 | user=> (realized? dd) 39 | false 40 | 41 | user=> @dd 42 | :done! 43 | 44 | user=> (realized? dd) 45 | true 46 | 47 | 48 | 并发线程 49 | -------------- 50 | 51 | ``future`` 将给定的表达式放到一个线程里执行,执行的结果使用解引用取出: 52 | 53 | :: 54 | 55 | user=> (def f (future 10086)) 56 | #'user/f 57 | 58 | user=> f 59 | # 60 | 61 | user=> @f 62 | 10086 63 | 64 | 解引用是否阻塞,取决于所给定的表达式在新线程里是否已经运行完毕。 65 | 66 | 比如以下代码就会阻塞几秒钟,因为它创建了一个会阻塞的表达式,并且\ **立即**\ 对它进行解引用: 67 | 68 | :: 69 | 70 | user=> @(future (Thread/sleep 5000) 10086) ; ... 需要等待 5 秒 71 | 10086 72 | 73 | 不论被包裹表达式是否阻塞,对 ``future`` 的调用总是立即返回的,调用者的线程并不会因为 ``future`` 所包裹的表达式而阻塞。 74 | 75 | .. note:: 76 | 77 | 为了进一步优化效率,被 ``future`` 包裹的表达式会被放到一个线程池里执行,而不是直接创建新线程。 78 | 79 | 80 | 数据流变量 81 | ------------ 82 | 83 | ``promise`` 声明某个变量为一个数据流变量,表示这个变量『会在将来的某个时候拥有一个值』。 84 | 85 | 带 ``promise`` 的变量的值通过 ``deliver`` 来设置。 86 | 87 | :: 88 | 89 | user=> (def p (promise)) 90 | #'user/p 91 | 92 | user=> p 93 | # 94 | 95 | user=> (deliver p 10086) 96 | # 97 | 98 | user=> @p 99 | 10086 100 | 101 | 当对一个未有值的 ``promise`` 变量进行求值时,当前线程会被阻塞,直到有其他线程对这个变量进行 ``deliver`` 为止。 102 | 103 | 104 | 并行 105 | ----------------- 106 | 107 | 使用线程,将计算任务分配到多个内核上并行执行,从而加快计算速度。 108 | 109 | pmap 110 | ^^^^^^ 111 | 112 | ``map`` 函数的并行版本。 113 | 114 | :: 115 | 116 | ; 串行运行, 4 个元素,每个等待 3 秒,共等待 12 秒 117 | user=> (time 118 | (dorun 119 | (map (fn [x] (Thread/sleep 3000)) 120 | (range 4)))) 121 | "Elapsed time: 12000.767484 msecs" 122 | nil 123 | 124 | ; 并行运行, 4 个元素,每个等待 3 秒,共等待 3 秒 125 | user=> (time 126 | (dorun 127 | (pmap (fn [x] (Thread/sleep 3000)) 128 | (range 4)))) 129 | "Elapsed time: 3002.602211 msecs" 130 | nil 131 | 132 | ``pmap`` 使用 ``future`` 实现,所以会产生一些和线程有关的消耗,如果要处理的操作并不耗时,就不要使用 ``pmap`` ,否则反而会影响性能。 133 | 134 | 135 | pcalls 136 | ^^^^^^^ 137 | 138 | 并行运行多个无参数函数,并以惰性序列的形式返回它们的值。 139 | 140 | :: 141 | 142 | ; 并行运行 3 个等待 3 秒的线程,共等待 3 秒 143 | user=> (pcalls 144 | #(Thread/sleep 3000) 145 | #(Thread/sleep 3000) 146 | #(Thread/sleep 3000)) 147 | (nil nil nil) 148 | 149 | 150 | pvalues 151 | ^^^^^^^^^ 152 | 153 | 并行对多个表达式进行求值,并以惰性序列的形式返回它们的值。 154 | 155 | :: 156 | 157 | ; 以下多个表达式的最大求值时间为 3 秒 158 | user=> (pvalues 159 | (Thread/sleep 3000) 160 | 10086 161 | (Thread/sleep 3000) 162 | "hello moto") 163 | (nil 10086 nil "hello moto") 164 | 165 | .. note:: 166 | 167 | 因为 ``pcalls`` 和 ``pvalues`` 的返回值都是惰性序列,因此,如果有一个非常耗时的表达式阻塞在其他一些表达式的前面,那么就算这些表达式已经计算完了,它们也不能被返回。 168 | 169 | 以下是这样一个实例,在序列前面的三个元素,可以立即被返回,但是,后面的三个元素只有等待 ``3`` 秒之后,才会被返回,尽管它们早就在并发线程里被求值完了: 170 | 171 | :: 172 | 173 | user=> (for [i (pvalues 1 2 3 (Thread/sleep 3000) 4 5 6)] (println i)) 174 | (1 175 | 2 176 | nil 3 ; 打印出这里之后,会停滞 3 秒 177 | nil nil 178 | nil 4 179 | nil 5 180 | nil 6 181 | nil nil) 182 | -------------------------------------------------------------------------------- /language/clojure/condition.rst: -------------------------------------------------------------------------------- 1 | .. highlight:: clojure 2 | 3 | 条件求值 4 | =========== 5 | 6 | if 7 | ---- 8 | 9 | ``if`` 接受三个单行表达式, ``test`` 、 ``then`` 和 ``else`` 。 10 | 11 | 如果 ``test`` 部分为真,那么就执行 ``then`` 部分;如果 ``test`` 部分为假,那么执行 ``else`` 部分。 12 | 13 | :: 14 | 15 | user=> (if true 16 | "is true" 17 | "is false") 18 | "is true" 19 | 20 | user=> (if false 21 | "is true" 22 | "is false") 23 | "is false" 24 | 25 | ``else`` 部分并不是必须的,如果省略 ``else`` 部分,那么条件为假时返回 ``nil`` 。 26 | 27 | :: 28 | 29 | user=> (if true "is true") 30 | "is true" 31 | 32 | user=> (if false "is true") 33 | nil 34 | 35 | 36 | do 37 | ------ 38 | 39 | ``do`` 可以包围任意数量的表达式,并按顺序求值这些表达式,然后返回最后一个表达式的值作为 ``do`` 的返回值,如果 ``do`` 里面没有表达式,那么返回 ``nil`` 。 40 | 41 | :: 42 | 43 | user=> (do 44 | (prn "hello") 45 | (prn "moto")) 46 | "hello" 47 | "moto" 48 | nil 49 | 50 | 利用 ``do`` ,我们可以让 ``if`` 也能处理多条语句: 51 | 52 | :: 53 | 54 | user=> (if true 55 | (do 56 | (prn "hello") 57 | (prn "moto"))) 58 | "hello" 59 | "moto" 60 | nil 61 | 62 | 63 | when 64 | -------- 65 | 66 | ``when`` 和 ``if`` 不同,它只处理条件部分为真时的情况,而且 ``when`` 的体内可以包括多条语句,而不是像 ``if`` 那样,只能处理单条语句: 67 | 68 | :: 69 | 70 | user=> (when true 71 | (prn "hello") 72 | (prn "moto")) 73 | "hello" 74 | "moto" 75 | nil 76 | 77 | 当条件部分为假时, ``when`` 只是简单地返回 ``nil`` : 78 | 79 | :: 80 | 81 | user=> (when false 82 | (prn "hello") 83 | (prn "moto")) 84 | nil 85 | 86 | 正如你所想象的, ``when`` 实际上就是 ``if`` 和 ``cond`` 组合构成得出的。 87 | 88 | 89 | cond 90 | -------- 91 | 92 | ``cond`` 接受任意数量的 ``test`` - ``exp`` 对,按顺序对各个 ``test`` 进行测试,并求值第一个为真 ``test`` 所对应的 ``exp`` 表达式;如果没有任何 ``test`` 为真,那么返回 ``nil`` 。 93 | 94 | :: 95 | 96 | (cond true "moto") ; => "moto" 97 | 98 | (cond false "hello" 99 | true "moto") ; => "moto" 100 | 101 | (cond false "hello" 102 | true "moto" 103 | true "google") ; => "moto" 104 | 105 | (cond false "hello") ; => nil 106 | 107 | 108 | if-not 、 when-not 109 | ---------------------- 110 | 111 | ``if-not`` 和 ``when-not`` 处理跟 ``if`` 和 ``when`` 相反的情况,它们只是 ``(if (not ...))`` 和 ``(when (not ...))`` 的一个快捷方式。 112 | 113 | :: 114 | 115 | user=> (if-not false "moto") 116 | "moto" 117 | 118 | user=> (when-not false "hello") 119 | "hello" 120 | 121 | 122 | if-let 、 when-let 123 | ---------------------- 124 | 125 | ``if-let`` 和 ``when-let`` 处理在条件判断前,先要创建局部变量的情况;它们分别是 ``(if (let ...))`` 和 ``(when (let ...))`` 的快捷方式: 126 | 127 | :: 128 | 129 | (if-let [test true] 130 | "is true" 131 | "is false" 132 | ) 133 | 134 | ; => "is true" 135 | 136 | (when-let [test true] 137 | "is true" 138 | ) 139 | ; => "is true" 140 | 141 | 要注意的一点是, ``if-let`` 允许创建多个局部变量,而 ``when-let`` 只能创建一个局部变量(这是由 ``when`` 的性质决定的)。 142 | 143 | .. for 144 | 145 | .. recur 146 | 147 | .. loop 148 | 149 | .. while 150 | -------------------------------------------------------------------------------- /language/clojure/index.rst: -------------------------------------------------------------------------------- 1 | .. highlight:: clojure 2 | 3 | Clojure 4 | ========== 5 | 6 | .. toctree:: 7 | :maxdepth: 2 8 | 9 | basic_type 10 | composite_type 11 | seq 12 | variable 13 | function 14 | condition 15 | namespace 16 | multimethod 17 | record 18 | protocol 19 | concurrency-and-parallelism 20 | reference-type 21 | misc 22 | -------------------------------------------------------------------------------- /language/clojure/misc.rst: -------------------------------------------------------------------------------- 1 | .. highlight:: clojure 2 | 3 | 杂项 4 | ========= 5 | 6 | 7 | 文件载入 8 | ---------- 9 | 10 | 对一些小程序进行原型测试的时候,通常要在 REPL 中进行一些重复的输入,一个更好的办法是将程序保存进某个文件,然后在 REPL 中通过载入文件来避免重复输入。 11 | 12 | ``load-file`` 函数读入并求值指定文件。 13 | 14 | 假如现在有文件 ``greet.clj`` ,内容如下: 15 | 16 | :: 17 | 18 | ;;; greet.clj 19 | 20 | (defn greeting [] 21 | (str "hello")) 22 | 23 | (greeting) 24 | 25 | 之后可以使用 ``load-file`` 对它进行读入和求值: 26 | 27 | :: 28 | 29 | user=> (load-file "greet.clj") 30 | "hello" 31 | 32 | 33 | 延迟求值 34 | ---------- 35 | 36 | Clojure 提供了一组原语,用于实现延迟求值。 37 | 38 | ``delay`` 延缓对一个表达式的求值,直到对它调用 ``force`` 为止: 39 | 40 | :: 41 | 42 | user=> (def d (delay (+ 1 1))) 43 | #'user/d 44 | 45 | user=> d 46 | # 47 | 48 | user=> (class d) 49 | clojure.lang.Delay 50 | 51 | user=> (force d) 52 | 2 53 | 54 | user=> d 55 | # 56 | 57 | user=> (class d) 58 | clojure.lang.Delay 59 | 60 | 61 | 异常的捕捉与处理 62 | ------------------ 63 | 64 | Clojure 的异常捕捉和处理沿用了 JAVA 的 ``try/catch/finally`` 套路,只是外观上略有不同,它的格式为: 65 | 66 | :: 67 | 68 | (try 69 | main-expr 70 | (catch exception-class exception-instance catch-expr*) 71 | (finally finally-expr*) 72 | ) 73 | 74 | 如果在 ``main-expr`` 执行的过程中,没有异常发生,那么返回 ``main-expr`` 的求值结果作为整个 ``try`` 表达式的值。 75 | 76 | 反之,如果有异常产生,那么程序就会将这个异常和所提供的 ``catch`` 语句进行匹配,看所抛出异常的类型是否为 ``exception-class`` 。 77 | 78 | 如果找到一个匹配正确的 ``catch`` 语句的话,那么就将这个异常实例和局部变量 ``exception-instance`` 绑定,这个局部变量可以在 ``catch-expr*`` 内使用。 79 | 80 | 当有异常发生时,被匹配 ``catch`` 语句的 ``catch-expr*`` 表达式的值就是整个 ``try`` 语句的返回值;如果没有提供 ``catch-expr*`` ,那么返回 ``nil`` 。 81 | 82 | ``catch`` 语句可以有多个,它们按从先到后的顺序被一个个匹配,如果所有语句都匹配不成功,那么程序向外部传递异常。 83 | 84 | ``finally`` 是可选的,最多只能有一个 ``finally`` 语句:它包围一些表达式,用它们的副作用来执行抛出异常之后要完成的动作,比如关闭数据库,等等。 85 | 86 | 举个例子,以下代码试图使用 ``Integer.`` 来将 ``"not a integer"`` 转换为整数,但这个字符串并不是合法的输入,于是引发异常: 87 | 88 | :: 89 | 90 | user=> (Integer. "not a integer") 91 | ; NumberFormatException For input string: "not a integer" 92 | ; java.lang.NumberFormatException.forInputString (NumberFormatException.java:65) 93 | 94 | 以下代码会捕捉异常,并打印出一个字符串格式的异常信息: 95 | 96 | :: 97 | 98 | user=> 99 | (try 100 | (Integer. "not a integer") 101 | (catch java.lang.NumberFormatException err (str err)) 102 | ) 103 | "java.lang.NumberFormatException: For input string: \"not a integer\"" 104 | -------------------------------------------------------------------------------- /language/clojure/multimethod.rst: -------------------------------------------------------------------------------- 1 | .. highlight:: clojure 2 | 3 | 多态方法 4 | =========== 5 | 6 | Clojure 通过多态方法(multimethod)来对运行时多态进行支持。 7 | 8 | 一个多态方法包含一个分派函数(dispatching function),以及一个或多个方法(method)。每个方法对应并处理一个分派值(dispatching value),分派值由分派函数产生。 9 | 10 | 如果一个分配值没有对应的方法,那么它的分配值为默认分派值(默认为 ``:default``\ ),如果多态方法没设置默认方法,那么抛出错误。 11 | 12 | 13 | 创建多态方法 14 | -------------- 15 | 16 | ``defmulti`` 用于定义多态方法,并设置分派函数。 17 | 18 | :: 19 | 20 | (defmulti complier :os) 21 | 22 | 以上代码定义了多态方法 ``complier`` ,并定义 ``:os`` 为它的分派函数。 23 | 24 | ``defmethod`` 创建并安装适用于给定分派值的新方法到给定的多态方法,以下代码定义了两个 ``complier`` 方法,它们的分派值分别是 ``::unix`` 和 ``::osx`` : 25 | 26 | :: 27 | 28 | (defmethod complier ::unix [m] 29 | (get m :complier) 30 | ) 31 | 32 | (defmethod complier ::oxs [m] 33 | (get m :complier) 34 | ) 35 | 36 | 对于不同的数据,多态方法 ``complier`` 返回不同的值: 37 | 38 | :: 39 | 40 | user=> (complier {:os ::unix :complier "cc"}) 41 | "cc" 42 | 43 | user=> (complier {:os ::osx :complier "gcc"}) 44 | "gcc" 45 | 46 | 47 | 派生 48 | ------- 49 | 50 | 可以注意到,前面用于处理 ``::unix`` 和 ``::osx`` 的方法实现实际上是完全一样的,这意味着,我们应该可以通过重用,减少其中一个方法的定义 —— ``drive`` 函数就是用来完成这事的:它可以让某个分派值的方法派生(derive)另一个分派值的方法,从而达到实现重用的目的。 51 | 52 | 比如说,我们大可不必为 ``::unix`` 和 ``::osx`` 两个分派值都编写方法,而是以让分派值为 ``::osx`` 的方法派生分派值 ``::unix`` 的方法,达到重用实现的目的: 53 | 54 | :: 55 | 56 | (defmethod complier ::unix [m] 57 | (get m :complier) 58 | ) 59 | 60 | (derive ::osx ::unix) 61 | 62 | 测试新的多态方法: 63 | 64 | :: 65 | 66 | user=> (complier {:os ::unix :complier "cc"}) 67 | "cc" 68 | 69 | user=> (complier {:os ::osx :complier "gcc"}) 70 | "gcc" 71 | 72 | 有一系列函数,比如 ``isa?`` 、 ``instance?`` 、 ``parents`` 、 ``ancestors`` 、 ``descendants`` 等函数,可以用于检查分派值之间的继承关系: 73 | 74 | :: 75 | 76 | user=> (isa? ::osx ::unix) 77 | true 78 | 79 | user=> (parents ::osx) 80 | #{:user/unix} 81 | 82 | user=> (parents ::unix) 83 | nil 84 | 85 | user=> (ancestors ::osx) 86 | #{:user/unix} 87 | 88 | user=> (descendants ::unix) 89 | #{:user/osx} 90 | 91 | 92 | 93 | 默认分派值 94 | ----------- 95 | 96 | 如果一个分派值没有对应的方法,并且该多态方法也没有设置默认方法,那么抛出一个错误: 97 | 98 | :: 99 | 100 | user=> (complier {:os ::linux :complier "gcc"}) 101 | IllegalArgumentException No method in multimethod 'complier' for dispatch value: :user/linux clojure.lang.MultiFn.getFn (MultiFn.java:121) 102 | 103 | 要防止这种错误,可以为多态方法设置默认方法。默认方法的设置非常简单,只要为分派值 ``:default`` 设置方法就可以了: 104 | 105 | :: 106 | 107 | (defmethod complier :default [m] 108 | (str "maybe is " (get m :complier)) 109 | ) 110 | 111 | 现在,来试试默认方法: 112 | 113 | :: 114 | 115 | user=> (complier {:os ::linux :complier "gcc"}) 116 | "maybe is gcc" 117 | 118 | user=> (complier {:os ::windows :complier "vc"}) 119 | "maybe is vc" 120 | 121 | 122 | 解决冲突 123 | ----------- 124 | 125 | 有时候,一个方法可能同时派生了两个方法,在调用这个方法时,就会产生冲突,因为 Clojure 不知道应该使用哪个方法。 126 | 127 | 考虑以下例子,分派值为 ``::c`` 的方法分别派生了分派值 ``::a`` 和 ``::b`` 的两个方法: 128 | 129 | :: 130 | 131 | (defmulti msg :tag) 132 | 133 | (defmethod msg ::a [m] 134 | "this is a" 135 | ) 136 | 137 | (defmethod msg ::b [m] 138 | "this is b" 139 | ) 140 | 141 | (derive ::c ::a) 142 | (derive ::c ::b) 143 | 144 | 这时分派值 ``::c`` 的方法调用就会产生冲突: 145 | 146 | :: 147 | 148 | user=> (msg {:tag ::c}) 149 | IllegalArgumentException Multiple methods in multimethod 'msg' match dispatch value: :user/c -> :user/b and :user/a, and neither is preferred clojure.lang.MultiFn.findAndCacheBestMethod (MultiFn.java:136) 150 | 151 | 要解决这种冲突,需要通过 ``prefer-method`` 函数显式调整两个祖先方法的优先级,让它们的其中一个优先成为 ``::c`` 分派值的方法,从而解决方法冲突: 152 | 153 | :: 154 | 155 | (defmulti msg :tag) 156 | 157 | (defmethod msg ::a [m] 158 | "this is a" 159 | ) 160 | 161 | (defmethod msg ::b [m] 162 | "this is b" 163 | ) 164 | 165 | (derive ::c ::a) 166 | (derive ::c ::b) 167 | 168 | (prefer-method msg ::a ::b) ; 新增 169 | 170 | 上面新增的代码表示,分派值 ``::a`` 的方法优先级高于分派值 ``::b`` 的方法,当出现二义性(ambiguous)时,就会优先使用分派值为 ``::a`` 的方法。 171 | 172 | 现在,再次调用 ``::c`` 分派值的方法,它就会使用 ``::a`` 分派值的方法: 173 | 174 | :: 175 | 176 | user=> (msg {:tag ::c}) 177 | "this is a" 178 | -------------------------------------------------------------------------------- /language/clojure/protocol.rst: -------------------------------------------------------------------------------- 1 | .. highlight:: clojure 2 | 3 | 协议 4 | ====== 5 | 6 | Clojure 中的协议用于为类型定义一集操作。 7 | 8 | 协议中的每个操作用一个方法(method)来定义,这个定义只提供方法的签名,但是并不包含该方法的实现。 9 | 10 | 举个例子,以下名为 ``QueueOperation`` 的协议定义了队列类型常见的 ``push`` 和 ``pop`` 操作: 11 | 12 | :: 13 | 14 | (defprotocol QueueOperation 15 | "队列的推入和弹出操作。" 16 | (push [queue item] 17 | "将元素 item 推入队列 queue 中, 18 | 新元素的摆放位置由队列的类型(FIFO/LIFP)来决定。" 19 | ) 20 | (pop [queue] 21 | "从队列 queue 中弹出一个元素, 22 | 弹出元素由队列的类型来决定, 23 | 如果队列为空,返回 nil 。" 24 | ) 25 | ) 26 | 27 | 因为一个协议可以被多种类型实现,而方法的分派(dispath)正是由方法的第一个参数的类型来决定的,因此协议中的方法必须至少带有一个参数(例子中是 ``queue`` )。 28 | 29 | 30 | 实现协议 31 | ----------- 32 | 33 | 前一节说过,协议可以由不同的类型来实现。 34 | 35 | 在前面介绍记录的时候我们说过,记录拥有自己的类型,这里我们先来定义一个记录,再为这个类型实现给定的协议。 36 | 37 | 一个基本的 FIFO (先进先出)队列的定义如下: 38 | 39 | :: 40 | 41 | (defrecord FifoQueue [container]) 42 | 43 | 我们可以用一个向量作为这个 FIFO 队列的容器,从而创建 ``FifoQueue`` 记录的实例: 44 | 45 | :: 46 | 47 | user=> (def q (->FifoQueue [1 2 3])) 48 | #'user/q 49 | 50 | user=> q 51 | #user.FifoQueue{:container [1 2 3]} 52 | 53 | 要正确地实现这个 ``FifoQueue`` ,程序必须在调用 ``push`` 时将新元素放到向量的末尾,比如 ``(push q 4)`` 应该会返回一个 ``:container`` 为 ``[1 2 3 4]`` 的记录。 54 | 55 | 另一方面, ``pop`` 操作被调用时,程序也需要将 ``:container`` 内的第一个元素弹出,比如说 ``(pop q)`` 应该会返回数字 ``3`` ,因为它是容器内的向量的最后一个元素。 56 | 57 | 以下就是 ``FifoQueue`` 类型对 ``QueueOperation`` 协议的实现: 58 | 59 | :: 60 | 61 | (extend-type FifoQueue ; 如果在 repl 中测试,那么全名为 user.FifoQueue 62 | QueueOperation 63 | (push [queue item] 64 | (let [ 65 | vector (:container queue) 66 | new-vector (conj vector item) 67 | ] 68 | (->FifoQueue new-vector) 69 | ) 70 | ) 71 | (pop [queue] 72 | (let [vector (:container queue)] 73 | (first vector) 74 | ) 75 | ) 76 | ) 77 | 78 | 以下代码测试了 ``FifoQueue`` 的实现: 79 | 80 | :: 81 | 82 | user=> (def q (->FifoQueue [1 2 3])) 83 | #'user/q 84 | 85 | user=> (push q 4) 86 | #user.FifoQueue{:container [1 2 3 4]} 87 | 88 | user=> (pop q) 89 | 1 90 | -------------------------------------------------------------------------------- /language/clojure/record.rst: -------------------------------------------------------------------------------- 1 | .. highlight:: clojure 2 | 3 | 记录 4 | ====== 5 | 6 | 7 | 类型的必要性 8 | ---------------- 9 | 10 | 在 Clojure 中,通常用 Map 来表示键-值对的数据,比如说,以下 Map 可以用于表示一条 tweet 信息: 11 | 12 | :: 13 | 14 | user=> (def t {:user "huangz" :content "hello world"}) 15 | #'user/t 16 | 17 | 在概念上,这个 Map 可以表示一条 tweet ,但是,从技术上来看,它只是一个 Map 而已: 18 | 19 | :: 20 | 21 | user=> (type t) 22 | clojure.lang.PersistentHashMap 23 | 24 | 在大多数时候,用 Map 来表示数据已经足够了,但是,在一些情况下,可能需要为一些数据创建新的类型,让它和其他结构相同的数据区别开来。 25 | 26 | 比如说,以下两个 Map 分别表示一个人和一只猫,但是,因为它们共享同样的结构,所以很容易会让人误以为是同类型的数据: 27 | 28 | :: 29 | 30 | user=> (def a-man {:name "peter" :age 25}) 31 | #'user/a-man 32 | 33 | user=> (def a-cat {:name "mimi" :age 5}) 34 | #'user/a-cat 35 | 36 | 在这种情况下,为了避免混淆,就需要创建新的类型来表示特定的数据了。 37 | 38 | 39 | 记录的创建和使用 40 | -------------------- 41 | 42 | Clojure 中的记录(record)可以用于创建带类型的键-值对数据结构,它的定义形式如下: 43 | 44 | :: 45 | 46 | (defrecord RecordName [key1 key2 ...]) 47 | 48 | 为了和函数名和宏名区别开来,记录的名字通常以大驼峰的形式给出。 49 | 50 | 继续前面的例子,对于人和猫,可以给出定义以下记录: 51 | 52 | :: 53 | 54 | (defrecord Man [name age]) 55 | 56 | (defrecord Cat [name age]) 57 | 58 | 定义记录之后,可以用 ``->RecordName`` 的形式创建相应记录的实例: 59 | 60 | :: 61 | 62 | user=> (def a-real-man (->Man "peter" 25)) 63 | #'user/a-real-man 64 | 65 | user=> (def a-real-cat (->Cat "mimi" 5)) 66 | #'user/a-real-cat 67 | 68 | user=> a-real-man 69 | #user.Man{:name "peter", :age 25} 70 | 71 | user=> a-real-cat 72 | #user.Cat{:name "mimi", :age 5} 73 | 74 | 可以用 ``(:field record)`` 的形式取出记录的域: 75 | 76 | :: 77 | 78 | user=> (:name a-real-man) 79 | "peter" 80 | 81 | user=> (:age a-real-man) 82 | 25 83 | 84 | .. warning:: 85 | 86 | Map 可以用 ``(:field map)`` 和 ``(map :field)`` 两种方式来取出域。 87 | 88 | 而记录的域只能用 ``(:field record)`` 的形式来取出。 89 | 90 | 以下是一个错误的例子: 91 | 92 | :: 93 | 94 | user=> (a-real-man :name) 95 | ClassCastException user.man cannot be cast to clojure.lang.IFn user/eval47 (NO_SOURCE_FILE:13) 96 | 97 | 和前面 Map 表示的例子一样, ``Man`` 和 ``Cat`` 两个记录含有同样的两个域: ``name`` 和 ``age`` 。 98 | 99 | 但是,记录和用 Map 表示的数据不一样,它拥有自己的类型: 100 | 101 | :: 102 | 103 | user=> (type a-real-man) 104 | user.Man 105 | 106 | user=> (type a-real-cat) 107 | user.Cat 108 | 109 | user=> (= (type a-real-man) (type a-real-cat)) 110 | false 111 | 112 | 这样就不会再混淆人和猫的数据了。 113 | -------------------------------------------------------------------------------- /language/clojure/reference-type.rst: -------------------------------------------------------------------------------- 1 | .. highlight:: clojure 2 | 3 | 引用类型 4 | ========= 5 | 6 | 7 | 通用设施 8 | --------- 9 | 10 | 所有引用类型都支持 watch 和 validator 两种机制,以下是对这两种机制的介绍。 11 | 12 | watch 13 | ^^^^^^^ 14 | 15 | Watch 监视给定的引用类型,并在引用类型的值改变时,执行给定的函数。 16 | 17 | ``add-watch`` 负责关联 watch 函数和引用类型,每个传给 ``add-watch`` 的函数都接受四个参数: 18 | 19 | 1. ``key`` :因为一个引用类型可以关联多个 watch 函数,所以需要使用一个 ``key`` 来标识各个不同的 watch 20 | 2. ``ref`` :发生改变的引用类型本身 21 | 3. ``old-state`` :引用类型的旧值 22 | 4. ``new-state`` :引用类型的新值 23 | 24 | 而 ``add-watch`` 本身接受三个函数: 25 | 26 | 1. 被关联的引用类型 27 | 2. watch 函数的 key 28 | 3. watch 函数 29 | 30 | :: 31 | 32 | user=> (defn show-change [key ref old-state new-state] 33 | (println "Watcher Key " key) 34 | (println "Watcher Ref " ref) 35 | (println "Watcher Old State " old-state) 36 | (println "Watcher New State " new-state)) 37 | #'user/show-change 38 | 39 | user=> (def a (atom {})) 40 | #'user/a 41 | 42 | user=> (add-watch a :show-info-when-value-change show-change) 43 | # 44 | 45 | user=> (swap! a assoc :key "value") 46 | Watcher Key :show-info-when-value-change 47 | Watcher Ref # 48 | Watcher Old State {} 49 | Watcher New State {:key value} 50 | {:key "value"} 51 | 52 | 一个引用类型可以关注多个 watch 函数,它们在引用类型的值被改变时同步地运行(执行的顺序不明,API文档没提到): 53 | 54 | :: 55 | 56 | user=> (def a (atom {})) 57 | #'user/a 58 | 59 | user=> (add-watch a :first-watcher (fn [key ref old new] (println "first watcher call"))) 60 | # 61 | 62 | user=> (add-watch a :second-watcher (fn [key ref old new] (println "second watcher call"))) 63 | # 64 | 65 | user=> (swap! a assoc :key "value") 66 | second watcher call 67 | first watcher call 68 | {:key "value"} 69 | 70 | ``remove-watch`` 函数用于移除关联的 watch 。 71 | 72 | 传给 ``remove-watch`` 的 ``key`` 参数需要和 ``add-watch`` 调用时指定的 ``key`` 一致: 73 | 74 | :: 75 | 76 | user=> (def a (atom {})) 77 | #'user/a 78 | 79 | user=> (add-watch a :watcher (fn [key ref old new] (println "watcher call"))) 80 | # 81 | 82 | user=> (swap! a assoc :key "value") 83 | watcher call 84 | {:key "value"} 85 | 86 | user=> (remove-watch a :watcher) 87 | # 88 | 89 | user=> (reset! a {}) 90 | {} 91 | -------------------------------------------------------------------------------- /language/clojure/seq.rst: -------------------------------------------------------------------------------- 1 | .. highlight:: clojure 2 | 3 | 序列 4 | ===== 5 | 6 | 序列是一个逻辑列表, Clojure 通过底层的 ``ISeq`` 接口来对序列进行支持,允许其他容器创建遍历自身元素的序列。每种 Clojure 容器都至少提供一种或以上的序列对象。 7 | 8 | 可以将序列看作是没有带状态指针、持久化和不可改版本的迭代器或枚举器。 9 | 10 | 11 | 序列接口 12 | ---------- 13 | 14 | 要使得容器支持序列操作,最起码要实现以下三个 API ,它们分别是 ``cons`` 、 ``first`` 和 ``rest`` 。 15 | 16 | ``(cons item seq)`` 创建一个包含新元素的序列: 17 | 18 | :: 19 | 20 | user=> (cons 1 '()) 21 | (1) 22 | 23 | user=> (cons 1 '(2 3)) 24 | (1 2 3) 25 | 26 | user=> (cons 1 '()) 27 | (1) 28 | 29 | user=> (cons 1 (cons 2 (cons 3 '()))) 30 | (1 2 3) 31 | 32 | ``(first coll)`` 返回序列中的第一个元素,如果传入序列为空,那么返回 ``nil`` : 33 | 34 | :: 35 | 36 | user=> (first '()) 37 | nil 38 | 39 | user=> (first '(1)) 40 | 1 41 | 42 | user=> (first '(1 2)) 43 | 1 44 | 45 | ``(rest coll)`` 返回序列中除第一个元素之外的其他元素,如果传入序列为空,那么返回空序列 ``'()`` : 46 | 47 | :: 48 | 49 | user=> (rest '()) 50 | () 51 | 52 | user=> (rest '(1)) 53 | () 54 | 55 | user=> (rest '(1 2)) 56 | (2) 57 | 58 | user=> (rest '(1 2 3)) 59 | (2 3) 60 | 61 | 62 | 创建序列对象 63 | -------------- 64 | 65 | 使用 ``seq`` 函数是创建序列对象最常用的方式,传入的参数可以是任何 Clojure 容器类型,或者是实现了 ``Iterable`` 接口的 JAVA 对象: 66 | 67 | :: 68 | 69 | user=> (seq "hello moto") 70 | (\h \e \l \l \o \space \m \o \t \o) 71 | 72 | user=> (seq (list 1 2 3)) 73 | (1 2 3) 74 | 75 | user=> (seq [1 2 3]) 76 | (1 2 3) 77 | 78 | user=> (seq {:clojure ".clj" :haskell ".hs" :scheme ".scm"}) 79 | ([:scheme ".scm"] [:clojure ".clj"] [:haskell ".hs"]) 80 | 81 | user=> (seq #{1 2 3}) 82 | (1 2 3) 83 | 84 | 还有一些其他创建列表对象的方法,比如 ``keys`` 和 ``vals`` ,以及 ``rseq`` ,诸如此类。 85 | 86 | 87 | 处理序列 88 | ---------- 89 | 90 | Clojure 提供了一集非常丰富的函数来对序列进行操作,比如常见的 ``map`` 、 ``filter`` 、 ``reduce`` 等等,在\ `Sequence 的文档页面 `_\ 详细地列出了这些 API 。 91 | 92 | 93 | 惰性序列 94 | --------- 95 | 96 | Clojure 实现了惰性序列,用于处理非常昂贵的计算,或者是无限序列。 97 | 98 | 创建一个惰性序列非常简单,只要用 ``lazy-seq`` 宏包裹一个创建 seq 对象的序列即可。 99 | 100 | 以下是一个惰性的 ``map`` 函数定义,它是内置 ``map`` 函数的简化版本: 101 | 102 | :: 103 | 104 | (defn lazy-map [f coll] 105 | (lazy-seq 106 | (when-let [s (seq coll)] 107 | (cons (f (first s)) 108 | (map f (rest s)))))) 109 | 110 | 测试 ``lazy-map`` : 111 | 112 | :: 113 | 114 | user=> (take 10 (lazy-map #(* % 2) (range))) 115 | (0 2 4 6 8 10 12 14 16 18) 116 | 117 | 所有作用于序列对象上的操作,都可以用于处理惰性序列。 118 | -------------------------------------------------------------------------------- /language/clojure/variable.rst: -------------------------------------------------------------------------------- 1 | .. highlight:: clojure 2 | 3 | 定义变量 4 | ========== 5 | 6 | Cljure 中的变量被称为 Var :每个 Var 以一个符号命名,并持有一个值。 7 | 8 | Var 的定义默认被所有线程所共享的,在某个线程里更改一个 Var 并不会改变这个 Var 原有的定义(这一定义被称为根绑定,root binding)。 9 | 10 | 11 | 全局变量 12 | ---------- 13 | 14 | ``def`` 特殊形式(special form)用于创建一个全局变量(global var)。 15 | 16 | :: 17 | 18 | user=> (def x 10086) 19 | #'user/x 20 | 21 | user=> x 22 | 10086 23 | 24 | 还可以给全局变量添加文档: 25 | 26 | :: 27 | 28 | user=> (def x "just a value, nothing else." 10086) 29 | #'user/x 30 | 31 | user=> (doc x) 32 | ------------------------- 33 | user/x 34 | just a value, nothing else. 35 | nil 36 | 37 | 38 | 私有变量 39 | ----------- 40 | 41 | 私有变量可以通过元数据的 ``private`` 关键字来声明: 42 | 43 | :: 44 | 45 | user=> (def ^{:private true} v 123) 46 | #'user/v 47 | 48 | user=> (meta #'v) ; 查看变量的私有变量 49 | {:ns #, :name v, :private true, :line 36, :file "NO_SOURCE_PATH"} 50 | 51 | 如果一个变量是私有的,那么其他命名空间就不可以在外部中引用这个变量。 52 | 53 | 54 | 局部变量 55 | --------------- 56 | 57 | 局部变量由 ``let`` 创建: 58 | 59 | :: 60 | 61 | user=> (let [v 123] 62 | v) 63 | 123 64 | 65 | ``let`` 中的多个声明按从先到后的顺序求值,因此,靠后的声明可以引用前面的声明: 66 | 67 | :: 68 | 69 | user=> (let [a 1 ; 1 70 | b (+ a 1) ; 2 71 | c (+ a b)] ; 3 72 | c) 73 | 3 74 | 75 | 当多个 ``let`` 嵌套时,嵌套最深的变量绑定先被使用: 76 | 77 | :: 78 | 79 | user=> (let [a 123] 80 | (let [a 10086] 81 | a)) 82 | 10086 83 | 84 | 在这种情况下,我们说,嵌套内部的 ``a`` 遮蔽(shadowed)了嵌套外部的 ``a`` 。 85 | 86 | 87 | 动态变量 88 | ----------- 89 | 90 | 默认情况下,Clojure 使用静态作用域来处理变量,使用 ``dynamic`` 元数据关键字,以及 ``binding`` 宏,可以将变量改为动态作用域: 91 | 92 | :: 93 | 94 | user=> (def ^:dynamic x 1) 95 | #'user/x 96 | 97 | user=> (def ^:dynamic y 2) 98 | #'user/y 99 | 100 | user=> (+ x y) 101 | 3 102 | 103 | user=> (binding [x 123 y 123] ; 使用动态绑定覆盖原来的绑定 104 | (+ x y)) 105 | 246 106 | 107 | user=> (+ x y) 108 | 3 109 | 110 | 111 | 单次赋值 112 | --------- 113 | 114 | ``defonce`` 提供了 ``def`` 的一个变种:当前仅当给定符号还没有绑定时,它才为这个符号设置绑定: 115 | 116 | :: 117 | 118 | user=> (defonce s "a string") 119 | #'user/s 120 | 121 | user=> s 122 | "a string" 123 | 124 | user=> (defonce s "another string") ; 设置无效 125 | nil 126 | 127 | user=> s 128 | "a string" 129 | -------------------------------------------------------------------------------- /language/mruby-code-analysis/index.rst: -------------------------------------------------------------------------------- 1 | MRuby 源码分析 2 | ======================= 3 | 4 | .. toctree:: 5 | :maxdepth: 2 6 | 7 | string 8 | -------------------------------------------------------------------------------- /network/distrubuted_systems_concepts_and_design/chp18.rst: -------------------------------------------------------------------------------- 1 | 第 18 章:复制(REPLICATION) 2 | ================================ 3 | 4 | Replication -- maintenance of copies of data at multiple computers: 5 | 6 | - key to the effectiveness of distributed systems 7 | 8 | - provide enhanced performance, high availability and fault tolerance 9 | 10 | - used widely, e.g. local web caching, DNS naming, etc. 11 | 12 | - a technique for enhancing services 13 | 14 | 15 | Motivations 16 | --------------- 17 | 18 | Motivations for replication include: 19 | 20 | Performance enhancement 21 | """""""""""""""""""""""""" 22 | 23 | - replication of immutable data is trivial:it increases performance with little cost to the system 24 | 25 | - replication of changing data incurs overheads in the form of protocols degined to ensure that clients receive up-to-date data: this limites to the effectiveness of replication as a performance-enhancement technique. 26 | 27 | Increased availability 28 | """""""""""""""""""""""""" 29 | 30 | Apart from delays due to pessimistic concurrency control conflicts (due to data locking), the factors that are relevant to high availability are: 31 | 32 | - server failures 33 | 34 | - replication is a technique for automatically maintaining the availability of data despite server failures 35 | 36 | - if data are replicated at two or more failure-independent servers, then client sofrware may be able to access data at an alternative server when the default server fail or become unreachable. 37 | 38 | - the percentage of time during which the SERVICE is available can be enhanced by replicating server data 39 | 40 | - if each of n servers has an independent probability p of failing or becoming unreachable, then the availability of an object stored at each of these servers is : :math:`1 - p^n` 41 | 42 | - network partitions and disconnected operation 43 | 44 | - user need to working in a disconnected environment 45 | 46 | - often prepare by copying heavily used data, such as the contents of a shared diary, from their usual environment to the laptop or mobile phone 47 | 48 | - when the user consults or updates the data, they risk reading data that someone else has altered in the meantime 49 | 50 | - disconnected working is ONLY feasible if the user (or the application on the user's behalf) can cope with stale data and can later reslove any conflicts that arise 51 | 52 | Fault tolerance 53 | """""""""""""""""""""""""" 54 | 55 | - high available data is NOT necessarily strictly correct data, it may be out of date, incomplete, or have some kind of error, etc. 56 | 57 | - a fault-tolerant service, by contrast, ALWAYS guarantees strictly correct behaviour despite a certain number and type of faults. 58 | 59 | - the correctness concerns : 60 | 61 | - the freshness of data supplied to the client, and 62 | 63 | - the effects of the client's operations upon the data, 64 | 65 | - sometimes also concerns the timeliness of the service's responses. 66 | 67 | - the same basic technique used for high availability -- that of replicating data and functionality between computes -- is also applicable for achieving fault tolerance: 68 | 69 | - more replication server, higher availability 70 | 71 | - but system must manage the coordination of its components precisely to maintain the correctness guarantees in the face of failures, which may occur at any time. 72 | 73 | 74 | Requirement 75 | ---------------- 76 | 77 | - replication transparency: 78 | 79 | - clients should not normally have to be aware that MULTIPLE PHYSICAL copies of data exists. 80 | 81 | - as far as clients are concerned, data are organized as INDIVIDUAL LOGICAL objects and they identify ONLY ONE item in each case when they request an operation to be performed. 82 | 83 | - clients expect operations to return ONLY ONE set of values. 84 | 85 | - consistency (can be vary in strength between applications): concerns wherther the operations performed upon a collection of replicaed objects produce result that meet the spcification of correctness for those objects. 86 | -------------------------------------------------------------------------------- /network/distrubuted_systems_concepts_and_design/index.rst: -------------------------------------------------------------------------------- 1 | 《DISTRIBUTED SYSTEMS Concepts and Design》笔记 2 | =================================================== 3 | 4 | 以下是《\ `DISTRIBUTED SYSTEMS Concepts and Design, 5e `_\ 》的笔记。 5 | 6 | .. toctree:: 7 | :maxdepth: 2 8 | 9 | chp18 10 | -------------------------------------------------------------------------------- /network/posav4/chp11.rst: -------------------------------------------------------------------------------- 1 | 11. Event Demultiplexing and Dispatching 2 | =============================================== 3 | 4 | Reactor 5 | ---------- 6 | 7 | .. image:: image/reactor.png 8 | :scale: 70 9 | -------------------------------------------------------------------------------- /network/posav4/image/reactor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huangzworks/note/82ce594784a1f31095fc7a40cc0ab11e863c5db4/network/posav4/image/reactor.png -------------------------------------------------------------------------------- /network/posav4/index.rst: -------------------------------------------------------------------------------- 1 | 《Pattern-Oriented Software Architecture, Volume 4: A Pattern Langauge for Distributed Computing》笔记 2 | =========================================================================================================== 3 | 4 | .. toctree:: 5 | :maxdepth: 2 6 | 7 | chp11.rst 8 | -------------------------------------------------------------------------------- /network/unp/chp1.rst: -------------------------------------------------------------------------------- 1 | .. highlight:: c 2 | 3 | 第 1 章:简介 4 | ================== 5 | 6 | 基本的时间服务器和客户端构建。 7 | 8 | 服务器代码: 9 | 10 | .. literalinclude:: code/chp1/daytimetcpsrv.c 11 | 12 | 运行服务器: 13 | 14 | :: 15 | 16 | $ sudo ./daytimetcpsrv 17 | time server start working! 18 | 19 | 客户端代码: 20 | 21 | .. literalinclude:: code/chp1/daytimetcpcli.c 22 | 23 | 运行客户端: 24 | 25 | :: 26 | 27 | $ ./daytimetcpcli 127.0.0.1 28 | Tue Jun 25 11:52:54 2013 29 | -------------------------------------------------------------------------------- /network/unp/chp2.rst: -------------------------------------------------------------------------------- 1 | 第 2 章:传输层:TCP 、 UDP 和 SCTP 2 | ============================================== 3 | 4 | 本章介绍的\ **传输层**\ 包括: 5 | 6 | - UDP :简单、不可靠的数据报协议 7 | 8 | - TCP :复杂、可靠的字节流协议 9 | 10 | - SCTP :和 TCP 一样,是一个可靠的传输协议,但它还提供消息边界、传输级别多宿(multihoming)支持,以及将头端阻塞(head-of-line blocking)减少到最小的一种方法。 11 | 12 | 以上协议层都转而使用\ **网络层**\ 协议 IP ,或是 IPv4 ,或是 IPv6 : 13 | 14 | .. image:: image/protocol.png 15 | 16 | 17 | 协议简介 18 | ----------- 19 | 20 | IPv4 21 | ^^^^^^^^^ 22 | 23 | 全名:网际协议版本 4 (Internet Protocol version 4)。 24 | 25 | - 自 80 年代早期以来的主要网际协议。 26 | 27 | - 使用 32 位地址。 28 | 29 | - 为 TCP 、 UDP 、 SCTP 、 ICMP 和 IGMP 提供分组递送服务。 30 | 31 | IPv6 32 | ^^^^^^^^^ 33 | 34 | 全名:网际协议版本 6 (Internet Protocol version 6). 35 | 36 | - 20 世纪 90 年代中期作为 IPv4 的替代品而设计。 37 | 38 | - 使用 128 位地址,以应对网络的爆发性增长。 39 | 40 | - 为 TCP 、 UDP 、 SCTP 和 ICMPv6 提供分组递送服务。 41 | 42 | UDP 43 | ^^^^^^ 44 | 45 | 全名:用户数据报协议(User Datagram Protocol)。 46 | 47 | - 一种无连接协议。 48 | 49 | - UDP 套接字是一种数据报套接字。 50 | 51 | - UDP 数据报不能保证最终到达它们的目的地。 52 | 53 | - 和 TCP 一样,既可以用 IPv4 也可以用 IPv6 。 54 | 55 | SCTP 56 | ^^^^^^^^ 57 | 58 | 全名:流控制传输协议(Stream Control Transmission Protocol)。 59 | 60 | - 提供可靠全双工关联的面向连接的协议。 61 | 62 | - 提供消息服务,也就是维护来自应用层的记录边界。 63 | 64 | - 既可以用 IPv4 也可以用 IPv6 ,而且能够在同一个关联中同时使用它们。 65 | 66 | .. note:: 关联 67 | 68 | 我们使用关联一词来指称 SCTP 中的链接, 69 | 因为 SCTP 是多宿的, 70 | 从而每个关联的两端涉及一组 IP 地址和一个端口号。 71 | 72 | 更多其他协议 73 | ^^^^^^^^^^^^^^^^ 74 | 75 | 书本还介绍了 ICMP 、 IGMP 、ARP 等协议, 76 | 具体参考书本。 77 | 78 | 79 | UDP 协议 80 | ----------- 81 | 82 | 从创建到发送 UDP 包的过程如下: 83 | 84 | 1. 应用程序往 UDP 套接字写域一个消息 85 | 86 | 2. 消息被封装到一个 UDP 数据报 87 | 88 | 3. UDP 数据报又被封装到一个 IP 数据报 89 | 90 | 4. 将 IP 送据报发送至目的地 91 | 92 | UDP 协议的特点: 93 | 94 | - 不保证 UDP 数据报会到达其最终目的地 95 | 96 | - 不保证各个数据报的先后顺序跨网络后保持不变 97 | 98 | - 不保证每个数据报只到达一次 99 | 100 | - 每个 UDP 数据报都有一个长度 101 | 102 | - 提供无连接服务: UDP 客户和服务器之间不必存在任何长期关系。 103 | 104 | 缺点 —— 缺乏可靠性: 105 | 106 | - 发送的过程中没有安全保证,出错也不会被源端(source)自动重传 107 | 108 | - 要确保一个数据报到达目的地,需要添加很多额外的特性 109 | 110 | 111 | TCP 协议 112 | ---------------- 113 | 114 | 特点: 115 | 116 | - 连接:TCP 客户先与某个给定服务器建立一个连接,再跨该连接与服务器交换数据。 117 | 118 | - 可靠性:当向另一端发送数据时,要求对端返回一个确认。如果没有收到确认,就重新发送并等待更长事件。TCP 会动态估算客户端和服务器之间的往返时间,知道应该等待多少时间。 119 | 120 | - 排序:对数据进行排序,以避免数据重复和混乱。 121 | 122 | - 流量控制:总是告知对端它一次能够接收多少字节,确保发送端发送的数据不会使接收缓冲区溢出。 123 | 124 | - 全双工:在一个给定的连接上,应用可以在任何时刻,在进出两个方向上既发送数据又接收数据。 125 | 126 | 127 | SCTP 128 | -------- 129 | 130 | 特点: 131 | 132 | - 在客户端和服务器之间提供关联:使用关联(association)而不是连接(connection),是因为一个关联指代两个系统之间的一次通讯,它可能因为 SCTP 支持多宿而设计不止两个地址。 133 | 134 | - 面向消息:提供按序投递服务。和 UDP 一样,由发送端写入每条记录的长度,并随数据一同传递给接收端。 135 | 136 | - 在端点之间提供多个流:每个流各自可靠地按序递送消息。一个流上某个消息的丢失不会阻塞同意关联其他流上的信息投递(TCP 在单个字节流中任何位置的字节丢失都将阻塞该连接上的所有数据的递送,直到该丢失数据被修复为止)。 137 | 138 | - 多宿:单个 SCTP 端点能够支持多个 IP 地址,可以通过切换地址来避免故障,增强健壮性。 139 | 140 | 141 | -------------------------------------------------------------------------------- /network/unp/chp3.rst: -------------------------------------------------------------------------------- 1 | .. highlight:: c 2 | 3 | 第 3 章:套接字编程简介 4 | ============================== 5 | 6 | IP echo 程序 7 | -------------- 8 | 9 | 用 ``inet_pton`` 和 ``inet_ntop`` 写的一个程序, 10 | 接收一个 ``ddd.ddd.ddd.ddd`` 格式的 IP 地址, 11 | 然后将它转换成 ``sockaddr_in`` 结构, 12 | 然后再从结构中转换出一个 IP 地址, 13 | 并打印出来。 14 | 15 | 如果一切正常的话, 16 | 打印的 IP 应该和输入的 IP 一样: 17 | 18 | .. literalinclude:: code/chp3/ip_echo.c 19 | 20 | 运行: 21 | 22 | :: 23 | 24 | $ ./a.out 123.123.123.123 25 | The ip you input is: 123.123.123.123 26 | 27 | -------------------------------------------------------------------------------- /network/unp/chp4.rst: -------------------------------------------------------------------------------- 1 | .. highlight:: c 2 | 3 | 第 4 章:基本 TCP 套接字编程 4 | ================================ 5 | 6 | getsockname & getpeername 7 | -------------------------------- 8 | 9 | 一个用 ``getsockname`` 和 ``getpeername`` 写成的打印服务器和客户端 IP 和端口的程序。 10 | 11 | 客户端: 12 | 13 | .. literalinclude:: code/chp4/test_getsockname_getpeername_client.c 14 | 15 | 运行客户端: 16 | 17 | :: 18 | 19 | $ ./client 127.0.0.1 20 | connect ok 21 | 22 | 服务器: 23 | 24 | .. literalinclude:: code/chp4/test_getsockname_getpeername_server.c 25 | 26 | 运行服务器: 27 | 28 | :: 29 | 30 | $ ./server 31 | Server ip is 0.0.0.0 , port is 52350 . 32 | Client ip is 127.0.0.1 , port is 60308 . 33 | 34 | -------------------------------------------------------------------------------- /network/unp/code/chp1/daytimetcpcli.c: -------------------------------------------------------------------------------- 1 | #include // socket 2 | #include // socket 3 | #include // printf 4 | #include // exit 5 | #include // bzero 6 | #include // htons, inet_pton 7 | 8 | #define MAXLINE (4096) 9 | 10 | int 11 | main(int argc, char *argv[]) 12 | { 13 | int sockfd, n; 14 | char recvline[MAXLINE + 1]; 15 | struct sockaddr_in servaddr; 16 | 17 | if (argc != 2) { 18 | fprintf(stderr, "usage: a.out \n"); 19 | exit(1); 20 | } 21 | 22 | if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { 23 | perror("socket error"); 24 | exit(2); 25 | } 26 | 27 | memset(&servaddr, 0, sizeof(servaddr)); 28 | 29 | servaddr.sin_family = AF_INET; 30 | servaddr.sin_port = htons(13); 31 | 32 | if (inet_pton(AF_INET, argv[1], &servaddr.sin_addr) <= 0) { 33 | fprintf(stderr, "inet_pton error for %s\n", argv[1]); 34 | exit(3); 35 | } 36 | 37 | if (connect(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr)) < 0) { 38 | perror("connect error"); 39 | exit(4); 40 | } 41 | 42 | while ((n = read(sockfd, recvline, MAXLINE)) > 0) { 43 | 44 | recvline[n] = '\0'; 45 | 46 | if (fputs(recvline, stdout) == EOF) { 47 | fprintf(stderr, "fputs error\n"); 48 | exit(5); 49 | } 50 | } 51 | 52 | if (n < 0) { 53 | perror("read error\n"); 54 | exit(6); 55 | } 56 | 57 | exit(0); 58 | } 59 | -------------------------------------------------------------------------------- /network/unp/code/chp1/daytimetcpsrv.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #define MAXLINE (4096) 10 | #define LISTENQ (1024) 11 | 12 | int 13 | main(int argc, char *argv[]) 14 | { 15 | int listenfd, connfd; 16 | struct sockaddr_in servaddr; 17 | char buff[MAXLINE]; 18 | time_t ticks; 19 | 20 | listenfd = socket(AF_INET, SOCK_STREAM, 0); 21 | 22 | memset(&servaddr, 0, sizeof(servaddr)); 23 | 24 | servaddr.sin_family = AF_INET; 25 | servaddr.sin_addr.s_addr = htonl(INADDR_ANY); 26 | servaddr.sin_port = htons(13); 27 | 28 | if (bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr)) == -1) { 29 | perror("bind error"); 30 | exit(1); 31 | } 32 | 33 | if (listen(listenfd, LISTENQ) == -1) { 34 | perror("listen error"); 35 | exit(2); 36 | } 37 | 38 | printf("time server start working!\n"); 39 | 40 | while (1) { 41 | 42 | if ((connfd = accept(listenfd, (struct sockaddr *)NULL, NULL)) == -1) { 43 | perror("accept error"); 44 | exit(3); 45 | } 46 | 47 | ticks = time(NULL); 48 | 49 | snprintf(buff, sizeof(buff), "%.24s\r\n", ctime(&ticks)); 50 | 51 | if (write(connfd, buff, strlen(buff)) == -1) { 52 | perror("write error"); 53 | exit(4); 54 | } 55 | 56 | close(connfd); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /network/unp/code/chp3/ip_echo.c: -------------------------------------------------------------------------------- 1 | #include // printf 2 | #include // exit, malloc 3 | #include // inet_ntop, inet_pton 4 | #include // struct sockaddr_in, AF_INET 5 | #include // strlen 6 | 7 | int 8 | main(int argc, char *argv[]) 9 | { 10 | char *buf; 11 | struct sockaddr_in addr; 12 | memset(&addr, 0, sizeof(struct sockaddr_in)); 13 | 14 | if (argc != 2) { 15 | fprintf(stderr, "usage: a.out \n"); 16 | exit(1); 17 | } 18 | 19 | // 将 ip 转换为地址 20 | 21 | if (inet_pton(AF_INET, argv[1], &addr.sin_addr) == -1) { 22 | perror("inet_pton error"); 23 | exit(2); 24 | } 25 | 26 | // 将地址转换为 ip 27 | 28 | buf = malloc(strlen(argv[1]) + 1); 29 | 30 | if (inet_ntop(AF_INET, &addr.sin_addr, buf, INET_ADDRSTRLEN) == NULL) { 31 | perror("inet_ntop error"); 32 | exit(3); 33 | } 34 | 35 | // 打印 ip 36 | 37 | printf("The input ip is: %s\n", buf); 38 | 39 | free(buf); 40 | 41 | return 0; 42 | } 43 | -------------------------------------------------------------------------------- /network/unp/code/chp4/cli: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huangzworks/note/82ce594784a1f31095fc7a40cc0ab11e863c5db4/network/unp/code/chp4/cli -------------------------------------------------------------------------------- /network/unp/code/chp4/client: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huangzworks/note/82ce594784a1f31095fc7a40cc0ab11e863c5db4/network/unp/code/chp4/client -------------------------------------------------------------------------------- /network/unp/code/chp4/server: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huangzworks/note/82ce594784a1f31095fc7a40cc0ab11e863c5db4/network/unp/code/chp4/server -------------------------------------------------------------------------------- /network/unp/code/chp4/test_getsockname_getpeername_client.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #define SERVER_PORT (52350) 9 | 10 | int main(int argc, char *argv[]) 11 | { 12 | int sockfd; 13 | struct sockaddr_in addr; 14 | 15 | if (argc != 2) { 16 | fprintf(stderr, "usage: a.out \n"); 17 | exit(1); 18 | } 19 | 20 | // 创建套接字 21 | sockfd = socket(AF_INET, SOCK_STREAM, 0); 22 | 23 | // 创建地址 24 | memset(&addr, 0, sizeof(addr)); 25 | addr.sin_family = AF_INET; 26 | inet_pton(AF_INET, argv[1], &addr.sin_addr); 27 | addr.sin_port = htons(SERVER_PORT); 28 | 29 | // 连接服务器 30 | if (connect(sockfd, (struct sockaddr *)&addr, sizeof(addr)) == -1) { 31 | perror("connect error"); 32 | exit(2); 33 | } 34 | 35 | printf("connect ok\n"); 36 | 37 | exit(0); 38 | } 39 | -------------------------------------------------------------------------------- /network/unp/code/chp4/test_getsockname_getpeername_server.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #define LISTENQ (1024) 9 | #define BUFSIZE (1024) 10 | 11 | #define SERVER_PORT (52350) 12 | 13 | void echo_ip_and_port(char *endpoint, struct sockaddr_in *addr); 14 | 15 | int main(void) 16 | { 17 | int listenfd, client_fd; 18 | socklen_t len; 19 | struct sockaddr_in addr, local_addr, client_addr; 20 | 21 | // 创建套接字 22 | listenfd = socket(AF_INET, SOCK_STREAM, 0); 23 | 24 | // 创建地址 25 | memset(&addr, 0, sizeof(addr)); 26 | addr.sin_family = AF_INET; 27 | addr.sin_addr.s_addr = htonl(INADDR_ANY); 28 | addr.sin_port = htons(SERVER_PORT); 29 | 30 | // 绑定地址 31 | bind(listenfd, (struct sockaddr *)&addr, sizeof(addr)); 32 | 33 | // 监听连接 34 | listen(listenfd, LISTENQ); 35 | 36 | // 接收连接 37 | client_fd = accept(listenfd, NULL, NULL); 38 | 39 | /* 40 | * 获取并打印服务器 ip 和端口 41 | */ 42 | memset(&local_addr, 0, sizeof(local_addr)); 43 | len = sizeof(local_addr); 44 | getsockname(listenfd, (struct sockaddr *)&local_addr, &len); 45 | 46 | echo_ip_and_port("Server", &local_addr); 47 | 48 | /* 49 | * 获取并打印客户端 ip 和端口 50 | */ 51 | memset(&client_addr, 0, sizeof(client_addr)); 52 | len = sizeof(client_addr); 53 | getpeername(client_fd, (struct sockaddr *)&client_addr, &len); 54 | 55 | echo_ip_and_port("Client", &client_addr); 56 | 57 | // 退出 58 | exit(0); 59 | } 60 | 61 | void echo_ip_and_port(char *endpoint, struct sockaddr_in *addr) { 62 | 63 | char buf[BUFSIZE]; 64 | 65 | printf("%s ip is %s , port is %d .\n", 66 | endpoint, 67 | inet_ntop(addr->sin_family, &addr->sin_addr, buf, INET_ADDRSTRLEN), 68 | ntohs(addr->sin_port)); 69 | } 70 | -------------------------------------------------------------------------------- /network/unp/image/protocol.dot: -------------------------------------------------------------------------------- 1 | digraph p { 2 | 3 | ip [label = "IPv4 或 IPv6"]; 4 | 5 | TCP -> ip; 6 | UDP -> ip; 7 | SCTP -> ip; 8 | } 9 | -------------------------------------------------------------------------------- /network/unp/image/protocol.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huangzworks/note/82ce594784a1f31095fc7a40cc0ab11e863c5db4/network/unp/image/protocol.png -------------------------------------------------------------------------------- /network/unp/index.rst: -------------------------------------------------------------------------------- 1 | 《Unix 网络编程》笔记 2 | ========================= 3 | 4 | .. toctree:: 5 | :maxdepth: 2 6 | 7 | chp1 8 | chp2 9 | chp3 10 | chp4 11 | -------------------------------------------------------------------------------- /os/apue/chp10.rst: -------------------------------------------------------------------------------- 1 | .. highlight:: c 2 | 3 | 第 10 章:信号 4 | ==================== 5 | 6 | 7 | 信号是软件中断,它提供了一种处理异步事件的手段。 8 | 9 | 10 | 信号的定义 11 | ---------------------- 12 | 13 | 每个信号都以一个 ``SIG`` 开头的命令定义,定义位于头文件 ```` 中。 14 | 15 | 信号都被定义为正整数,不存在编号为 ``0`` 的信号,因为发送信号的 ``kill`` 函数对 ``0`` 有特殊的用途(用于检查进程是否存在)。 16 | 17 | 18 | 产生信号 19 | ------------ 20 | 21 | 有很多条件可以产生信号: 22 | 23 | - 用户通过按键,引发终端产生信号:比如 CTRL+C 可以产生中断信号 ``SIGINT`` 。 24 | 25 | - 硬件异常产生信号:除零、无效的内存引用等等。这些异常由硬件检测,并通知内核,内核再为引发问题的进程产生适当的信号。 26 | 27 | - 进程调用 ``kill(2)`` 函数可将信号发送给另一个进程或进程组。 28 | 29 | - 用户用 ``kill(1)`` 命令将信号发送给其他进程。 30 | 31 | - 当检测到某种软件条件已经发生,也会产生信号来通知进行。 32 | 33 | .. note:: 这里使用的 ``kill`` 是带有歧义的,它的意思是“向进程发送指定信号”,而不是“杀死某个程序”。 34 | 35 | 36 | 信号处理 37 | ------------ 38 | 39 | 信号是异步事件的一个典型示例, 40 | 不能通过检测某个变量来判断是否出现了一个信号, 41 | 而是必须告诉内核, 42 | “当XX信号发生时,请执行以下动作”。 43 | 44 | 处理信号有以下三种方法: 45 | 46 | - 忽略此信号,不执行任何动作。除了用于终结进程的 ``SIGKILL`` 和 ``SIGSTOP`` 之外,大部分信号都可以被忽略。 47 | 48 | - 捕捉信号。通知内核,在指定信号发生时,执行一个指定的函数,这个函数称为该信号的信号处理器(handler)。 49 | 50 | - 执行系统默认动作。最常见的默认动作是终结接收到信号的进程。 51 | 52 | 53 | 信号处理器 54 | ------------- 55 | 56 | 使用 ``signal`` 函数,可以为指定信号管理一个信号处理器:当进程接到给定信号时,它就会执行所指定的信号处理器。 57 | 58 | ``signal`` 函数的签名定义如下: 59 | 60 | :: 61 | 62 | #include 63 | 64 | typedef void (*sighandler_t)(int); 65 | 66 | sighandler_t signal(int signum, sighandler_t handler); 67 | 68 | 其中参数 ``signum`` 指定要处理的信号; 69 | ``handler`` 参数指定处理信号的函数, 70 | 这个函数接受一个整数值,但不返回任何值。 71 | 72 | 如果处理器设置成功, ``signal`` 函数的返回值是之前为这个信号所设置的信号处理器。如果设置失败,则返回 ``SIG_ERR`` 。 73 | 74 | ``handler`` 可以是 ``SIG_IGN`` 或者 ``SIG_DFL`` ,前者表示忽略信号,后者表示执行信号的默认动作。 75 | 76 | 以下程序执行一个无限循环,直到接受到指定信号才退出: 77 | 78 | .. literalinclude:: code/10-signal.c 79 | 80 | 我们在后台运行这个程序,并调用 ``kill`` 命令向它发送指定的信号: 81 | 82 | :: 83 | 84 | $ ./10-signal.out & 85 | [1] 4016 86 | 87 | $ kill -USR1 4016 88 | receive signal 10 89 | bye bye ~ 90 | 91 | .. tip:: ``signal`` 函数的语义和实现有关,最好使用 ``sigaction`` 函数代替 ``signal`` 函数。 92 | 93 | 94 | 信号与阻塞调用 95 | ------------------ 96 | 97 | 如果在进程执行一个阻塞调用的过程中, 98 | 系统捕捉到一个信号, 99 | 那么这个调用就会被中断, 100 | 不再执行, 101 | 该系统调用返回出错, 102 | 并将 ``errno`` 设为 ``EINTR`` 。 103 | 104 | 以下代码展示了一个可能被中断的读操作如何重新启动: 105 | 106 | :: 107 | 108 | again: 109 | if ((n = read(fd, buf, BUFFSIZE)) < 0) { 110 | if (errno = EINTR) 111 | goto again; 112 | else 113 | // handle error 114 | } 115 | 116 | 117 | 发送信号 118 | ----------- 119 | 120 | 有几个函数可以用于发送和等待信号。 121 | 122 | ``kill`` 和终端的 ``kill`` 命令类似,它可以将一个指定的信号发送给指定的进程或者进程组(实际上, ``kill`` 命令就由 ``kill`` 函数定义),它的函数签名定义如下: 123 | 124 | :: 125 | 126 | #include 127 | #include 128 | 129 | int kill(pid_t pid, int sig); 130 | 131 | ``raise`` 可以向调用进程自身发送一个信号: 132 | 133 | :: 134 | 135 | #include 136 | 137 | int raise(int sig); 138 | 139 | ``alarm`` 可以在规定的时间之后,向调用进程自身发送一个 ``SIGALRM`` 信号: 140 | 141 | :: 142 | 143 | #include 144 | 145 | unsigned int alarm(unsigned int seconds); 146 | 147 | 因为 ``SIGALRM`` 的默认操作是终止进程,因此,要有效地利用 ``alarm`` ,通常需要为 ``SIGALRM`` 信号设置一个信号处理程序。 148 | 149 | ``pause`` 函数挂起调用进程,直到捕捉到一个信号为止: 150 | 151 | :: 152 | 153 | #include 154 | 155 | int pause(void); 156 | 157 | 158 | -------------------------------------------------------------------------------- /os/apue/chp11.rst: -------------------------------------------------------------------------------- 1 | .. highlight:: c 2 | 3 | 第 11 章:线程 4 | ================== 5 | 6 | 线程是一个进程中的控制和执行单元(unit), 7 | 传统的 UNIX 进程可以看成是只有一个控制线程: 8 | 在同一时刻,它只能做一件事。 9 | 10 | 通过在进程中创建多个线程, 11 | 可以并发地完成多个不同的任务。 12 | 13 | 进程的所有信息对该进程的所有线程都是共享的, 14 | 包括可执行的程序文本、程序的全局内存和堆内存、栈以及文件描述符。 15 | 16 | 多线程的用途和好处包括: 17 | 18 | - 使用同步代码,实现异步效果。 19 | 20 | - 实现简单的进程间通讯和数据共享。 21 | 22 | - 将多个任务并行化,提高效率,减少阻塞。比如一个编辑器可以用一个线程接受用户的输入,另一个线程写入数据,第三个线程进行备份,等等。 23 | 24 | 当然,多线程也带来了一些难题, 25 | 其中最重要的就是因为共享数据而带来的数据一致性问题, 26 | 以及线程之间的同步,等等。 27 | 28 | .. note:: 29 | 30 | 多线程和多处理器并没有直接关系, 31 | 即使使用的是单处理器,也可以从多线程中获益。 32 | 33 | 34 | 创建线程 35 | ------------ 36 | 37 | ``pthread_create`` 函数用于创建新线程: 38 | 39 | :: 40 | 41 | #include 42 | 43 | // 创建成功返回 0 ,否则返回错误编号 44 | int pthread_create(pthread_t *thread, // 保存新线程的 id 45 | const pthread_attr_t *attr, // 定制线程的属性 46 | void *(*start_routine) (void *), // 线程要执行的函数 47 | void *arg); // 传给函数的参数 48 | 49 | 和创建进程类似, 50 | 系统并不保证新线程和原有线程之间那个会先执行, 51 | 所以线程程序一定要有相应的同步措施, 52 | 并且线程所执行的程序不能依赖于特定的执行顺序。 53 | 54 | 另外, 55 | 线程相关函数出错时, 56 | 通常以返回错误代码而不是设置 ``errno`` 的方式来报告错误, 57 | 这样更简洁一些。 58 | 59 | ``pthread_self`` 函数用于返回线程的 id ,同一个进程的线程 id 是唯一的: 60 | 61 | :: 62 | 63 | #include 64 | 65 | pthread_t pthread_self(void); 66 | 67 | 以下是书本提供的,创建新线程并打印线程 id 的示例代码: 68 | 69 | .. literalinclude:: code/11-1.c 70 | 71 | 以下是代码的执行结果(记得编译的时候要带上 ``-lpthread`` ): 72 | 73 | :: 74 | 75 | $ ./11-1.out 76 | main thread: pid 3729 tid 3075602112 (0xb751f6c0) 77 | new thread: pid 3729 tid 3075599168 (0xb751eb40) 78 | 79 | 80 | 终止线程 81 | ---------- 82 | 83 | 线程有以下两种外部/自然终止方式: 84 | 85 | - 进程中的任一线程调用了 ``exit`` 、 ``_Exit`` 或者 ``_exit`` ,那么整个进程终止。 86 | 87 | - 如果信号的默认动作是终止进程,那么把该信号发送到某个线程会导致整个进程终止。 88 | 89 | 还有以下三种线程主动终止的方式: 90 | 91 | - 线程执行完毕,从调用例程中返回,终止它的控制流,但不影响整个进程。 92 | 93 | - 线程被同一进程的其他线程取消。 94 | 95 | - 线程调用 ``pthread_exit`` 。 96 | 97 | 除非某个线程是进程的最后一个线程, 98 | 否则, 99 | 当一个线程终止时, 100 | 它所共享的进程资源不会被释放, 101 | ``atexit`` 也不会被调用。 102 | 103 | ``pthread_exit`` 的签名如下: 104 | 105 | :: 106 | 107 | #include 108 | 109 | void pthread_exit(void *retval); 110 | 111 | ``retval`` 用于向使用 ``pthread_join`` 等待这个线程的另一个线程返回值。 112 | 113 | ``pthread_join`` 将调用线程阻塞,直到指定的线程终止为止,它的签名如下: 114 | 115 | :: 116 | 117 | #include 118 | 119 | // 成功返回 0 ,否则返回错误代码 120 | int pthread_join(pthread_t thread, void **retval); 121 | 122 | ``thread`` 是等待的目标线程, ``retval`` 用于保存目标线程的返回值。 123 | 124 | 以下是书本提供的,测试线程退出的例程: 125 | 126 | .. literalinclude:: code/11-2.c 127 | 128 | 执行结果: 129 | 130 | :: 131 | 132 | $ ./11-2.out 133 | thread 1 returning 134 | thread 2 exiting 135 | thread 1 exit code 0 136 | thread 2 exit code 2 137 | 138 | 139 | 参考资料 140 | ------------ 141 | 142 | https://computing.llnl.gov/tutorials/pthreads 143 | 144 | 一个教程,很规范地列举了关于线程、mutex、条件变量等知识,以及 API 的使用方式。 145 | -------------------------------------------------------------------------------- /os/apue/chp14.rst: -------------------------------------------------------------------------------- 1 | .. highlight:: c 2 | 3 | 第 14 章:高级 I/O 4 | ====================== 5 | 6 | 14.2 非阻塞 I/O 7 | ----------------- 8 | 9 | **低速系统调用** —— *可能会使进程永远阻塞*\ 的一类系统调用,包括: 10 | 11 | * | 如果某类文件类型(例如管道、终端设备和网络设备)的数据并不存在,则读操作会使调用者永远阻塞。 12 | | [huangz:一般可以通过设置最长等待时间(timeout)来解决这类问题。] 13 | 14 | * 如果数据不能立即被上述同类型的文件接受(由于在管道中无空间、网络流控制等),则写操作也会使调用者永远阻塞。 15 | 16 | * | 在某种条件发生之前,打开某些类型的文件会被阻塞。 17 | | 例如打开一个终端设备可能需要到与之连接的调制解调器应答; 18 | | 又例如在没有其他进程已用读模式打开该 FIFO 时若以只写模式打开 FIFO ,那么也要等待。 19 | 20 | * 对已经上锁的文件进行读/写。 21 | 22 | * 某些 ``ioctl`` 操作。 23 | 24 | * 某些进程间通信函数(见第 15 章) 25 | 26 | --- 27 | 28 | **非阻塞 I/O** 使我们可以调用 ``open`` 、 ``read`` 和 ``write`` 这样的 I/O 操作,并使这些操作不会永远阻塞。 29 | 如果这种操作不能完成,则调用立即出错返回,表示该操作如继续执行将阻塞。 30 | 31 | --- 32 | 33 | 有两种方法可以让给定的文件描述符使用非阻塞形式: 34 | 35 | 1. 在调用 ``open`` 打开文件时,执行 ``O_NONBLOCK`` 标志 36 | 2. 如果描述符已经被打开,那么使用 ``fcntl`` 函数打开 ``O_NONBLOCK`` 标志(见3.14节) 37 | 38 | .. note:: 39 | 40 | POSIX.1 要求,对于一个非阻塞的描述符,如果无数据可读,则 ``read`` 返回 ``-1`` ,并设置 ``errno`` 为 ``EAGAIN`` 。 41 | 42 | .. note:: 43 | 44 | ``O_NONBLOCK`` 影响共享同一文件表的所有用户,但与通过其他文件表对同一设备的访问无关(见图 3-1 和图 3-3)。 45 | 46 | --- 47 | 48 | 非阻塞 I/O 实例: 49 | 50 | .. literalinclude:: code/14-1.c 51 | 52 | 输出: 53 | 54 | :: 55 | 56 | $ ./14-1.out < /bin/bash 2>14-1-err.out 57 | 58 | $ head 14-1-err.out 59 | read 500000 bytes 60 | nwrite = 6144, errno = 0 // 成功 61 | nwrite = -1, errno = 11 // 失败 (AGAIN) 62 | nwrite = -1, errno = 11 63 | nwrite = -1, errno = 11 64 | nwrite = -1, errno = 11 65 | nwrite = -1, errno = 11 66 | nwrite = -1, errno = 11 67 | nwrite = -1, errno = 11 68 | nwrite = -1, errno = 11 69 | 70 | 在这个实例中,程序发出了数千个 ``write`` 调用,但是只有非常小的一部分额是真正输出数据的,其余的都是出错返回。 71 | 72 | 这种形式的循环成为\ **轮询**\ ,它的缺点是浪费了 CPU 时间。14.5 节将介绍非阻塞描述符的 I/O 多路转接,这是进行这种操作的一种比较有效的方法。 73 | 74 | 通过多线程,可以避免使用阻塞 I/O :即使一个线程被阻塞了,另一个线程也可以继续工作。不过多线程的使用增加了复杂性,很容易造成得不偿失的结果。 75 | -------------------------------------------------------------------------------- /os/apue/code/10-signal.c: -------------------------------------------------------------------------------- 1 | // 10-signal.c 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | void sig_usr(int); 8 | 9 | int 10 | main(void) { 11 | 12 | if (signal(SIGUSR1, sig_usr) == SIG_ERR) { 13 | printf("set SIGUSR1 handler fail\n"); 14 | return 1; 15 | } 16 | 17 | for ( ; ; ) 18 | pause(); 19 | } 20 | 21 | void 22 | sig_usr(int signo) 23 | { 24 | printf("receive signal %d \n", SIGUSR1); 25 | printf("bye bye ~\n"); 26 | exit(0); 27 | } 28 | -------------------------------------------------------------------------------- /os/apue/code/11-1.c: -------------------------------------------------------------------------------- 1 | // 11-1.c 2 | 3 | #include "apue.h" 4 | #include 5 | 6 | pthread_t ntid; 7 | 8 | // 打印进程 id 和线程 id 9 | void 10 | printids(const char *s) 11 | { 12 | pid_t pid = getpid(); 13 | 14 | pthread_t tid = pthread_self(); 15 | 16 | printf("%s pid %u tid %u (0x%x)\n", 17 | s, (unsigned int) pid, (unsigned int) tid, (unsigned int) tid); 18 | } 19 | 20 | // 新线程的执行函数 21 | void * 22 | thr_fn(void *arg) 23 | { 24 | printids("new thread: "); 25 | return ((void *)0); 26 | } 27 | 28 | int 29 | main(void) 30 | { 31 | int err = pthread_create(&ntid, NULL, thr_fn, NULL); 32 | if (err != 0) 33 | err_quit("can't create thread: %s\n", strerror(err)); 34 | 35 | printids("main thread:"); 36 | 37 | // 等待子线程完成 38 | sleep(1); 39 | 40 | exit(0); 41 | } 42 | -------------------------------------------------------------------------------- /os/apue/code/11-2.c: -------------------------------------------------------------------------------- 1 | // 11-2.c 2 | 3 | #include "apue.h" 4 | #include 5 | 6 | void * 7 | thr_fn1(void *arg) 8 | { 9 | printf("thread 1 returning\n"); 10 | return ((void *) 0); 11 | } 12 | 13 | void * 14 | thr_fn2(void *arg) 15 | { 16 | printf("thread 2 exiting\n"); 17 | pthread_exit((void *) 2); 18 | } 19 | 20 | int 21 | main(void) 22 | { 23 | int err; 24 | pthread_t tid1, tid2; 25 | void *tret; 26 | 27 | // 创建线程 1 28 | err = pthread_create(&tid1, NULL, thr_fn1, NULL); 29 | if (err != 0) 30 | err_quit("create thread 1 fail\n", strerror(err)); 31 | 32 | // 创建线程 2 33 | err = pthread_create(&tid2, NULL, thr_fn2, NULL); 34 | if (err != 0) 35 | err_quit("create thread 2 fail\n", strerror(err)); 36 | 37 | // 等待线程 1 执行完毕 38 | err = pthread_join(tid1, &tret); 39 | if (err != 0) 40 | err_quit("join thread 1 fail\n", strerror(err)); 41 | printf("thread 1 exit code %d\n", (int) tret); 42 | 43 | // 等待线程 2 执行完毕 44 | err = pthread_join(tid2, &tret); 45 | if (err != 0) 46 | err_quit("join thread 2 fail\n", strerror(err)); 47 | printf("thread 2 exit code %d\n", (int) tret); 48 | 49 | exit(0); 50 | } 51 | -------------------------------------------------------------------------------- /os/apue/code/14-1.c: -------------------------------------------------------------------------------- 1 | /* 2 | * 14-1.c 3 | */ 4 | #include "apue.h" 5 | #include 6 | #include 7 | 8 | char buf [500000]; 9 | 10 | int 11 | main(void) 12 | { 13 | int ntowrite, nwrite; 14 | char *ptr; 15 | 16 | ntowrite = read(STDIN_FILENO, buf, sizeof(buf)); 17 | fprintf(stderr, "read %d bytes\n", ntowrite); 18 | 19 | // 设置非阻塞标志 20 | set_fl(STDOUT_FILENO, O_NONBLOCK); 21 | 22 | ptr = buf; 23 | while (ntowrite > 0) { 24 | errno = 0; 25 | nwrite = write(STDOUT_FILENO, ptr, ntowrite); 26 | // 每次都将写出字节数和错误号码打印到标准输出 27 | fprintf(stderr, "nwrite = %d, errno = %d\n", nwrite, errno); 28 | 29 | if (nwrite > 0) { 30 | ptr += nwrite; 31 | ntowrite -= nwrite; 32 | } 33 | } 34 | 35 | // 清除非阻塞 36 | clr_fl(STDOUT_FILENO, O_NONBLOCK); 37 | 38 | exit(0); 39 | } 40 | -------------------------------------------------------------------------------- /os/apue/code/3-close.c: -------------------------------------------------------------------------------- 1 | // 3-close.c 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | int main(void) { 8 | 9 | char filename[] = "test-3-close"; 10 | mode_t mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH; // 644 11 | 12 | int fd = creat(filename, mode); 13 | 14 | if (fd == -1) { 15 | printf("creat file fail\n"); 16 | return 1; 17 | } 18 | else { 19 | printf("creat file OK\n"); 20 | } 21 | 22 | if (close(fd) == -1) { 23 | printf("close file fail\n"); 24 | return 2; 25 | } 26 | else { 27 | printf("close file OK\n"); 28 | } 29 | 30 | return 0; 31 | } 32 | -------------------------------------------------------------------------------- /os/apue/code/3-creat.c: -------------------------------------------------------------------------------- 1 | // 3-creat.c 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | int main(void) { 9 | 10 | char filename[] = "test-3-creat"; 11 | 12 | mode_t mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH; // 644 13 | 14 | if (creat(filename, mode) == -1) { 15 | printf("creat file fail\n"); 16 | return 1; 17 | } 18 | else { 19 | printf("creat file OK\n"); 20 | return 0; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /os/apue/code/3-dup.c: -------------------------------------------------------------------------------- 1 | // 3-dup.c 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | void main(void) { 9 | 10 | // 1) dup 11 | 12 | int d1 = dup(STDIN_FILENO); 13 | 14 | printf("file descriptor duplicated by dup, number = %d\n", d1); 15 | 16 | 17 | // 2) dup2 18 | 19 | int newfd = 123; 20 | int d2 = dup2(STDIN_FILENO, newfd); 21 | 22 | printf("file descriptor duplicated by dup2 with newfd %d, number = %d\n", newfd, d2); 23 | 24 | 25 | // 3) dup4 26 | 27 | int newfd_2 = 456; 28 | int d3= dup3(STDIN_FILENO, newfd_2, O_CLOEXEC); 29 | 30 | printf("file descriptor duplicated by dup3 with newfd %d, number = %d\n", newfd_2, d3); 31 | } 32 | -------------------------------------------------------------------------------- /os/apue/code/3-hole.c: -------------------------------------------------------------------------------- 1 | // 3-hole.c 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #define FIRST_CONTENT "abcdefg" 10 | #define SECOND_CONTENT "hello moto" 11 | 12 | #define HOLE_LENGTH 256 13 | 14 | int main(void) { 15 | 16 | // 1) creat file 17 | 18 | char filename[] = "test-3-hole"; 19 | mode_t mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH; // 644 20 | 21 | int fd = creat(filename, mode); 22 | 23 | if (fd == -1) { 24 | printf("creat file fail\n"); 25 | return 1; 26 | } 27 | 28 | // 2) write first content 29 | 30 | if (write(fd, FIRST_CONTENT, sizeof(FIRST_CONTENT)) != sizeof(FIRST_CONTENT)) { 31 | printf("write first content fail\n"); 32 | return 2; 33 | } 34 | 35 | // 3) make hole 36 | 37 | lseek(fd, HOLE_LENGTH, SEEK_CUR); 38 | 39 | // 4) write second content 40 | 41 | if (write(fd, SECOND_CONTENT, sizeof(SECOND_CONTENT)) != sizeof(SECOND_CONTENT)) { 42 | printf("write second content fail\n"); 43 | return 2; 44 | } 45 | 46 | return 0; 47 | } 48 | -------------------------------------------------------------------------------- /os/apue/code/3-lseek.c: -------------------------------------------------------------------------------- 1 | // 3-lseek.c 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #define CONTENT "hello moto" 10 | #define LEN_OF_CONTENT sizeof(CONTENT) 11 | 12 | int main(void) { 13 | 14 | // 1) creat file 15 | 16 | char filename[] = "test-3-lseek"; 17 | mode_t mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH; // 644 18 | 19 | // use open instead of creat for read and write 20 | int fd = open(filename, O_RDWR, mode); 21 | 22 | if (fd == -1) { 23 | printf("creat file fail\n"); 24 | return 1; 25 | } 26 | 27 | // 2) write file 28 | 29 | if (write(fd, CONTENT, LEN_OF_CONTENT) != LEN_OF_CONTENT) { 30 | printf("write error\n"); 31 | return 2; 32 | } 33 | 34 | // 3) set offset to 0 35 | 36 | if (lseek(fd, 0, SEEK_SET) == -1) { 37 | printf("lseek error\n"); 38 | return 3; 39 | } 40 | 41 | // 4) read and print conent 42 | 43 | char buf[LEN_OF_CONTENT]; 44 | 45 | if (read(fd, buf, LEN_OF_CONTENT) != LEN_OF_CONTENT) { 46 | printf("read error\n"); 47 | return 4; 48 | } 49 | 50 | printf("file conent: %s\n", buf); 51 | 52 | return 0; 53 | } 54 | -------------------------------------------------------------------------------- /os/apue/code/3-open-create-file.c: -------------------------------------------------------------------------------- 1 | // 3-open-create-file.c 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | int main(void) { 10 | 11 | char filename[] = "test-3-open-create-file.txt"; 12 | 13 | int flag = O_RDWR | O_CREAT; // open/create for read and write 14 | 15 | mode_t mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH; // 644 16 | 17 | if (open(filename, flag, mode) == -1) { 18 | printf("open/create file fail\n"); 19 | printf("errno = %d\n", errno); 20 | return 1; 21 | } 22 | 23 | return 0; 24 | } 25 | -------------------------------------------------------------------------------- /os/apue/code/3-open-create-with-excl.c: -------------------------------------------------------------------------------- 1 | // 3-open-create-with-excl.c 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | int main(void) { 10 | 11 | char filename[] = "test-3-open-create-with-excl.txt"; 12 | 13 | // create for read and write 14 | // and make sure this call create the file 15 | int flag = O_RDWR | O_CREAT | O_EXCL; 16 | 17 | mode_t mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH; // 644 18 | 19 | if (open(filename, flag, mode) == -1) { 20 | if (errno == EEXIST) { 21 | printf("open fail cause file already exists.\n"); 22 | } else { 23 | printf("open/create file fail\n"); 24 | printf("errno = %d\n", errno); 25 | } 26 | return 1; 27 | } 28 | 29 | return 0; 30 | } 31 | -------------------------------------------------------------------------------- /os/apue/code/3-pread.c: -------------------------------------------------------------------------------- 1 | // 3-pread.c 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #define BUF_SIZE 1024 10 | 11 | #define READ_SIZE 10 12 | 13 | off_t get_current_offset(int fd); 14 | 15 | int main(int argc, char *argv[]) { 16 | 17 | // 1) check argument 18 | 19 | if (argc != 2) { 20 | printf("usage: ./a.out file\n"); 21 | return 1; 22 | } 23 | 24 | 25 | // 2) open file 26 | 27 | char *filename = argv[1]; 28 | 29 | int fd = open(filename, O_RDONLY); 30 | 31 | if (fd == -1) { 32 | printf("open file %s fail\n", filename); 33 | return 2; 34 | } 35 | 36 | 37 | // 3) read 38 | 39 | off_t before_read_offset = get_current_offset(fd); 40 | 41 | char buf[BUF_SIZE]; 42 | 43 | if (read(fd, buf, READ_SIZE) == -1) { 44 | printf("read fail\n"); 45 | return 3; 46 | } 47 | 48 | off_t after_read_offset = get_current_offset(fd); 49 | 50 | printf("after read %d bytes from file, offset move from %lld to %lld\n", 51 | READ_SIZE, 52 | (long long)before_read_offset, 53 | (long long)after_read_offset); 54 | 55 | 56 | // 4) pread 57 | 58 | off_t before_pread_offset = get_current_offset(fd); 59 | 60 | if (pread(fd, buf, READ_SIZE, before_pread_offset) == -1) { 61 | printf("pread fail\n"); 62 | return 4; 63 | } 64 | 65 | off_t after_pread_offset = get_current_offset(fd); 66 | 67 | printf("after pread %d bytes from file, offset move from %lld to %lld\n", 68 | READ_SIZE, 69 | (long long)before_pread_offset, 70 | (long long)after_pread_offset); 71 | 72 | return 0; 73 | } 74 | 75 | off_t get_current_offset(int fd) { 76 | return lseek(fd, 0, SEEK_CUR); 77 | } 78 | -------------------------------------------------------------------------------- /os/apue/code/3-print-file-descriptor.c: -------------------------------------------------------------------------------- 1 | // 3-print-file-descriptor.c 2 | 3 | #include 4 | #include 5 | 6 | int main(void) { 7 | 8 | char filename[] = "test-3-print-file-descriptor"; 9 | 10 | int flag = O_RDWR | O_CREAT; 11 | 12 | mode_t mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH; // 644 13 | 14 | int fd = open(filename, flag, mode); 15 | if (fd == -1) { 16 | printf("open/create file fail\n"); 17 | return 1; 18 | } 19 | else { 20 | printf("fd = %d\n", fd); 21 | return 0; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /os/apue/code/3-read.c: -------------------------------------------------------------------------------- 1 | // 3-read.c 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #define CONTENT "hello moto" 10 | #define LEN_OF_CONTENT sizeof(CONTENT) 11 | 12 | int main(void) { 13 | 14 | // 1) create file 15 | 16 | char filename[] = "test-3-read"; 17 | mode_t mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH; // 644 18 | 19 | int fd = creat(filename, mode); 20 | 21 | if (fd == -1) { 22 | printf("creat file fail\n"); 23 | return 1; 24 | } 25 | 26 | 27 | // 2) write file 28 | 29 | if (write(fd, CONTENT, LEN_OF_CONTENT) != LEN_OF_CONTENT) { 30 | printf("write error\n"); 31 | return 2; 32 | } 33 | 34 | close(fd); 35 | 36 | 37 | // 3) reopen and read 38 | 39 | fd = open(filename, O_RDONLY); 40 | 41 | if (fd == -1) { 42 | printf("open file fail\n"); 43 | return 3; 44 | } 45 | 46 | char buf[LEN_OF_CONTENT]; 47 | 48 | if (read(fd, buf, LEN_OF_CONTENT) != LEN_OF_CONTENT) { 49 | printf("read file fail\n"); 50 | return 4; 51 | } 52 | 53 | 54 | // 4) print content 55 | 56 | printf("file content: %s\n", buf); 57 | 58 | return 0; 59 | } 60 | -------------------------------------------------------------------------------- /os/apue/code/3-seek-data-and-hole.c: -------------------------------------------------------------------------------- 1 | // 3-seek-data-and-hole.c 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | int main(int argc, char *argv[]) { 8 | 9 | // 1) check filename input 10 | 11 | if (argc != 2) { 12 | printf("usage: ./out filename\n"); 13 | return 1; 14 | } 15 | 16 | // 2) open file for read 17 | 18 | char *filename = argv[1]; 19 | int fd = open(filename, O_RDONLY); 20 | 21 | if (fd == -1) { 22 | printf("open file fail\n"); 23 | return 2; 24 | } 25 | 26 | // 3) seek to first data 27 | 28 | off_t data_offset; 29 | 30 | data_offset = lseek(fd, 0, SEEK_DATA); 31 | if (data_offset == -1) { 32 | printf("get data offset fail\n"); 33 | return 4; 34 | } 35 | 36 | printf("first data offset of file %s is %lld\n", filename, (long long)data_offset); 37 | 38 | // 4) seek to first hole 39 | 40 | off_t hole_offset; 41 | 42 | hole_offset = lseek(fd, 0, SEEK_HOLE); 43 | if (hole_offset == -1) { 44 | printf("get hole offset fail\n"); 45 | return 5; 46 | } 47 | 48 | printf("first hole offset of file %s is %lld\n", filename, (long long)hole_offset); 49 | 50 | return 0; 51 | } 52 | -------------------------------------------------------------------------------- /os/apue/code/3-show-hole.c: -------------------------------------------------------------------------------- 1 | // 3-show-hole.c 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #define FIRST_CONTENT "abcdefg" 10 | #define SECOND_CONTENT "hello moto" 11 | 12 | #define HOLE_LENGTH 256 13 | 14 | #define BUF_SIZE 1024 15 | 16 | int main(int argc, char *argv[]) { 17 | 18 | // 1) open file 19 | 20 | if (argc != 2) { 21 | printf("usage: ./out file-with-hole\n"); 22 | return 1; 23 | } 24 | 25 | char *filename = argv[1]; 26 | int fd = open(filename, O_RDONLY); 27 | 28 | if (fd == -1) { 29 | printf("open file fail\n"); 30 | return 2; 31 | } 32 | 33 | // 2) iterate file 34 | 35 | int read_status; 36 | char buf[BUF_SIZE]; 37 | off_t current_offset, data_offset, hole_offset; 38 | 39 | while (1) { 40 | 41 | read_status = read(fd, buf, BUF_SIZE); 42 | 43 | if (read_status == -1) { 44 | printf("read error\n"); 45 | return 3; 46 | } 47 | 48 | if (read_status == 0) { 49 | printf("EOF\n"); 50 | return 0; 51 | } 52 | 53 | // inspect buf to decide what to seek next 54 | if (buf[0] == '\0') { 55 | // current offset pointing a hole 56 | // seek to next data 57 | current_offset = lseek(fd, 0, SEEK_CUR); 58 | if (current_offset == -1) { 59 | printf("get offset fail\n"); 60 | return 4; 61 | } 62 | 63 | data_offset = lseek(fd, current_offset, SEEK_DATA); 64 | if (data_offset == -1) { 65 | printf("data seek fail\n"); 66 | return 4; 67 | } 68 | 69 | printf("data start from offset %ld\n", data_offset); 70 | } 71 | else { 72 | // current offset pointing data 73 | // seek to next hole 74 | current_offset = lseek(fd, 0, SEEK_CUR); 75 | if (current_offset == -1) { 76 | printf("get offset fail\n"); 77 | return 5; 78 | } 79 | 80 | hole_offset = lseek(fd, current_offset, SEEK_HOLE); 81 | if (hole_offset == -1) { 82 | printf("hole seek fail\n"); 83 | return 5; 84 | } 85 | 86 | printf("hole start from offset %ld\n", hole_offset); 87 | } 88 | 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /os/apue/code/3-write.c: -------------------------------------------------------------------------------- 1 | // 3-write.c 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #define CONTENT "hello moto\n" 9 | #define LEN_OF_CONTENT sizeof(CONTENT) 10 | 11 | int main(void) { 12 | 13 | char filename[] = "test-3-write"; 14 | mode_t mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH; // 644 15 | 16 | int fd = creat(filename, mode); 17 | 18 | if (fd == -1) { 19 | printf("creat file FAIL\n"); 20 | return 1; 21 | } 22 | 23 | int write_byte = write(fd, CONTENT, LEN_OF_CONTENT); 24 | 25 | if (write_byte != LEN_OF_CONTENT) { 26 | printf("write file FAIL\n"); 27 | return 2; 28 | } 29 | 30 | return 0; 31 | } 32 | -------------------------------------------------------------------------------- /os/apue/code/4-change-owner.c: -------------------------------------------------------------------------------- 1 | // 4-change-owner.c 2 | 3 | #include // chown 4 | #include // printf, perror 5 | 6 | #define ROOT_UID 0 7 | #define ROOT_GID 0 8 | 9 | int main(int argc, char *argv[]) { 10 | 11 | // 1) 12 | 13 | if (argc != 2) { 14 | printf("usage: ./a.out filepath"); 15 | return 1; 16 | } 17 | 18 | // 2) 19 | 20 | char *filepath = argv[1]; 21 | 22 | if (chown(filepath, ROOT_UID, ROOT_GID) == -1) { 23 | perror("Change owner fail"); 24 | return 2; 25 | } 26 | 27 | // 3) 28 | 29 | return 0; 30 | } 31 | -------------------------------------------------------------------------------- /os/apue/code/4-owner.c: -------------------------------------------------------------------------------- 1 | // 4-owner-c 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | int main(int argc, char *argv[]) { 8 | 9 | // 1) check argument 10 | 11 | if (argc != 2) { 12 | printf("usage: ./a.out filepath\n"); 13 | return 1; 14 | } 15 | 16 | // 2) get file info 17 | 18 | struct stat file; 19 | char *filepath = argv[1]; 20 | 21 | if (stat(filepath, &file) == -1) { 22 | printf("get file %s info fail\n", filepath); 23 | return 2; 24 | } 25 | 26 | // 3) print owner info 27 | 28 | printf("file owner uid = %u , gid = %u .\n", file.st_uid, file.st_gid); 29 | 30 | // 4) 31 | 32 | return 0; 33 | } 34 | -------------------------------------------------------------------------------- /os/apue/code/4-permission.c: -------------------------------------------------------------------------------- 1 | // 4-permission.c 2 | 3 | #include 4 | #include 5 | 6 | #define READ_PERMISSION 4 7 | #define WRITE_PERMISSION 2 8 | #define EXECUTE_PERMISSION 1 9 | 10 | #define RWX_PERMISSION 7 11 | 12 | int main(int argc, char *argv[]) { 13 | 14 | // 1) check argument 15 | 16 | if (argc != 2) { 17 | printf("usage: ./a.out filepath"); 18 | return 1; 19 | } 20 | 21 | // 2) get file info 22 | 23 | struct stat file; 24 | char *filepath = argv[1]; 25 | 26 | if (stat(filepath, &file) == -1) { 27 | printf("get file info fail\n"); 28 | return 2; 29 | } 30 | 31 | // 3) test permission 32 | 33 | unsigned int owner = 0, group = 0, other = 0; 34 | 35 | // owner test 36 | if (file.st_mode & S_IRWXU == S_IRWXU) { 37 | owner = RWX_PERMISSION; 38 | } 39 | else { 40 | if (file.st_mode & S_IRUSR) 41 | owner += READ_PERMISSION; 42 | 43 | if (file.st_mode & S_IWUSR) 44 | owner += WRITE_PERMISSION; 45 | 46 | if (file.st_mode & S_IXUSR) 47 | owner += EXECUTE_PERMISSION; 48 | } 49 | 50 | // group test 51 | 52 | if (file.st_mode & S_IRWXG == S_IRWXG) { 53 | group = RWX_PERMISSION; 54 | } 55 | else { 56 | if (file.st_mode & S_IRGRP) 57 | group += READ_PERMISSION; 58 | 59 | if (file.st_mode & S_IWGRP) 60 | group += WRITE_PERMISSION; 61 | 62 | if (file.st_mode & S_IXGRP) 63 | group += EXECUTE_PERMISSION; 64 | } 65 | 66 | // other test 67 | 68 | if (file.st_mode & S_IRWXO == S_IRWXO) { 69 | other = RWX_PERMISSION; 70 | } 71 | else { 72 | if (file.st_mode & S_IROTH) 73 | other += READ_PERMISSION; 74 | 75 | if (file.st_mode & S_IWOTH) 76 | other += WRITE_PERMISSION; 77 | 78 | if (file.st_mode & S_IXOTH) 79 | other += EXECUTE_PERMISSION; 80 | } 81 | 82 | // 4) print permission 83 | 84 | printf("file %s permission = %u%u%u\n", filepath, owner, group, other); 85 | 86 | return 0; 87 | } 88 | -------------------------------------------------------------------------------- /os/apue/code/4-print-size.c: -------------------------------------------------------------------------------- 1 | // 4-print-size.c 2 | 3 | #include // perror, fprintf, printf 4 | #include // lstat, struct stat, S_ISREG, S_ISLNK 5 | 6 | int main(int argc, char *argv[]) { 7 | 8 | if (argc != 2) { 9 | fprintf(stderr, "usage: ./a.out filepath\n"); 10 | return 1; 11 | } 12 | 13 | char *filepath = argv[1]; 14 | struct stat file; 15 | 16 | // use lstat() instead of stat() 17 | // not follow if filepath is a symbolic link 18 | if (lstat(filepath, &file) == -1) { 19 | perror("Get file info fail"); 20 | return 2; 21 | } 22 | 23 | if (S_ISREG(file.st_mode) || S_ISLNK(file.st_mode)) 24 | printf("%s length = %lld bytes .\n", filepath, (long long)file.st_size); 25 | else 26 | printf("%s is not regular file or symbolic link , length unknow.\n", filepath); 27 | 28 | return 0; 29 | } 30 | -------------------------------------------------------------------------------- /os/apue/code/4-print-type-2.c: -------------------------------------------------------------------------------- 1 | // 4-printf-type-2.c 2 | 3 | #include 4 | #include 5 | 6 | int main(int argc, char *argv[]) { 7 | 8 | if (argc != 2) { 9 | printf("usage: ./a.out filepath\n"); 10 | return 1; 11 | } 12 | 13 | char *filepath = argv[1]; 14 | struct stat file; 15 | 16 | if (stat(filepath, &file) == -1) { 17 | printf("get file %s info fail\n", filepath); 18 | return 2; 19 | } 20 | 21 | switch (file.st_mode & S_IFMT) { 22 | case S_IFREG: 23 | printf("regular\n"); 24 | break; 25 | case S_IFDIR: 26 | printf("directory\n"); 27 | break; 28 | case S_IFCHR: 29 | printf("character device\n"); 30 | break; 31 | case S_IFBLK: 32 | printf("block device\n"); 33 | break; 34 | case S_IFIFO: 35 | printf("FIFO\n"); 36 | break; 37 | case S_IFLNK: 38 | printf("symbolic link\n"); 39 | break; 40 | case S_IFSOCK: 41 | printf("socket\n"); 42 | break; 43 | default: 44 | printf("unknow type\n"); 45 | } 46 | 47 | return 0; 48 | } 49 | 50 | -------------------------------------------------------------------------------- /os/apue/code/4-print-type.c: -------------------------------------------------------------------------------- 1 | // 4-print-type.c 2 | 3 | #include 4 | #include 5 | 6 | int main(int argc, char *argv[]) { 7 | 8 | if (argc != 2) { 9 | printf("usage: ./a.out filepath\n"); 10 | return 1; 11 | } 12 | 13 | char *filepath = argv[1]; 14 | struct stat file; 15 | 16 | if (stat(filepath, &file) == -1) { 17 | printf("get file %s info fail\n", filepath); 18 | return 2; 19 | } 20 | 21 | if (S_ISREG(file.st_mode)) { 22 | printf("regular\n"); 23 | } 24 | else if (S_ISDIR(file.st_mode)) { 25 | printf("directory\n"); 26 | } 27 | else if (S_ISCHR(file.st_mode)) { 28 | printf("character device\n"); 29 | } 30 | else if (S_ISBLK(file.st_mode)) { 31 | printf("block device\n"); 32 | } 33 | else if (S_ISFIFO(file.st_mode)) { 34 | printf("FIFO\n"); 35 | } 36 | else if (S_ISLNK(file.st_mode)) { 37 | printf("symbolic link\n"); 38 | } 39 | else if (S_ISSOCK(file.st_mode)) { 40 | printf("socket\n"); 41 | } 42 | else { 43 | printf("unknow type\n"); 44 | } 45 | 46 | return 0; 47 | } 48 | -------------------------------------------------------------------------------- /os/apue/code/4-set-owner-x-permission.c: -------------------------------------------------------------------------------- 1 | // 4-set-x-permission.c 2 | 3 | #include 4 | #include 5 | 6 | int main(int argc, char *argv[]) { 7 | 8 | // 9 | 10 | if (argc != 2) { 11 | fprintf(stderr, "usage: ./a.out filepath\n"); 12 | return 1; 13 | } 14 | 15 | // 16 | 17 | char *filepath = argv[1]; 18 | struct stat file; 19 | 20 | if (stat(filepath, &file) == -1) { 21 | perror("Get file info fail\n"); 22 | return 2; 23 | } 24 | 25 | if (chmod(filepath, file.st_mode | S_IXUSR) == -1) { 26 | perror("Set owner execute permission fail"); 27 | return 3; 28 | } 29 | 30 | return 0; 31 | } 32 | -------------------------------------------------------------------------------- /os/apue/code/4-special-permission.c: -------------------------------------------------------------------------------- 1 | // 4-special-permission.c 2 | 3 | #include 4 | #include 5 | 6 | void print_set_or_not(char *bit_name, int is_set); 7 | 8 | int main(int argc, char *argv[]) { 9 | 10 | // check argument 11 | 12 | if (argc != 2) { 13 | fprintf(stderr, "usage: ./a.out filepath\n"); 14 | return 1; 15 | } 16 | 17 | // get file info 18 | 19 | char *filepath = argv[1]; 20 | struct stat file; 21 | 22 | if (stat(filepath, &file) == -1) { 23 | perror("Get file info fail"); 24 | return 2; 25 | } 26 | 27 | // check regular file 28 | 29 | if (S_ISREG(file.st_mode)) { 30 | 31 | print_set_or_not( 32 | "set-user-ID", 33 | file.st_mode & S_ISUID == S_ISUID 34 | ); 35 | 36 | print_set_or_not( 37 | "set-group-ID", 38 | file.st_mode & S_ISGID == S_ISGID 39 | ); 40 | } 41 | 42 | // check directory 43 | 44 | if (S_ISDIR(file.st_mode)) { 45 | 46 | print_set_or_not( 47 | "sticky", 48 | file.st_mode & S_ISVTX == S_ISVTX 49 | ); 50 | } 51 | 52 | return 0; 53 | } 54 | 55 | void print_set_or_not(char *bit_name, int is_set) { 56 | 57 | printf("%s bit is ", bit_name); 58 | 59 | if (is_set) 60 | printf("set"); 61 | else 62 | printf("not set"); 63 | 64 | printf("\n"); 65 | } 66 | -------------------------------------------------------------------------------- /os/apue/code/4-truncate.c: -------------------------------------------------------------------------------- 1 | // 4-truncate.c 2 | 3 | #include // truncate 4 | #include // stderr, fprintf, perror 5 | #include // atoll 6 | 7 | int main(int argc, char *argv[]) { 8 | 9 | if (argc != 3) { 10 | fprintf(stderr, "usage: ./a.out filepath new_size\n"); 11 | return 1; 12 | } 13 | 14 | char *filepath = argv[1]; 15 | long long int new_size = atoll(argv[2]); 16 | 17 | if (truncate(filepath, new_size) == -1) { 18 | perror("Truncate fail"); 19 | return 2; 20 | } 21 | 22 | return 0; 23 | } 24 | -------------------------------------------------------------------------------- /os/apue/code/8-data-duplicate.c: -------------------------------------------------------------------------------- 1 | // 8-data-duplicate.c 2 | 3 | #include 4 | #include 5 | 6 | int global = 0; 7 | 8 | int main(void) { 9 | 10 | int local = 0; 11 | 12 | printf("init global = %d , local = %d\n", global, local); 13 | 14 | pid_t child_pid = fork(); 15 | 16 | if (child_pid == 0) { 17 | global++; 18 | local++; 19 | 20 | printf("child running, global = %d , local = %d\n", global, local); 21 | } 22 | else if (child_pid > 0) { 23 | global++; 24 | local++; 25 | 26 | printf("parent running, global = %d , local = %d\n", global, local); 27 | } 28 | else { 29 | printf("Fork error\n"); 30 | } 31 | 32 | return 0; 33 | } 34 | -------------------------------------------------------------------------------- /os/apue/code/8-exec.c: -------------------------------------------------------------------------------- 1 | // 8-exec.c 2 | 3 | #include 4 | #include 5 | 6 | int main(void) { 7 | 8 | pid_t pid = fork(); 9 | 10 | if (pid == -1) { 11 | printf("fork error\n"); 12 | return 1; 13 | } 14 | else if (pid == 0) { 15 | // child 16 | execl("hello_world.out", (char *)NULL); 17 | } 18 | 19 | return 0; 20 | } 21 | -------------------------------------------------------------------------------- /os/apue/code/8-fork.c: -------------------------------------------------------------------------------- 1 | // 8-fork.c 2 | 3 | #include 4 | #include 5 | 6 | int main(void) { 7 | 8 | pid_t child_id; 9 | 10 | child_id = fork(); 11 | 12 | if (child_id == 0) { 13 | printf("Child process running, id = %d\n", getpid()); 14 | printf("Child process's parent id = %d\n", getppid()); 15 | } 16 | else if (child_id > 0) { 17 | printf("Parent process running, id = %d\n", getpid()); 18 | } 19 | else { 20 | // child_id == -1 21 | printf("Fork error\n"); 22 | } 23 | 24 | return 0; 25 | } 26 | -------------------------------------------------------------------------------- /os/apue/code/8-get-exit-status.c: -------------------------------------------------------------------------------- 1 | // 8-get-exit-status.c 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | int main(void) { 8 | 9 | int status; 10 | pid_t pid = fork(); 11 | 12 | if (pid == -1) { 13 | printf("fork error\n"); 14 | return 1; 15 | } 16 | 17 | if (pid != 0) { 18 | // parent 19 | wait(&status); 20 | if (WIFEXITED(status)) { 21 | printf("child id %d ,normal terminated with status %d .\n", pid, WEXITSTATUS(status)); 22 | } 23 | else { 24 | printf("child not terminate normaly, something went wrong\n"); 25 | } 26 | } 27 | else { 28 | // child 29 | return 2; // status 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /os/apue/code/8-getpid.c: -------------------------------------------------------------------------------- 1 | // 8-getpid.c 2 | 3 | #include 4 | #include 5 | 6 | int main(void) { 7 | 8 | printf("Process id = %d\n", getpid()); 9 | 10 | } 11 | -------------------------------------------------------------------------------- /os/apue/code/8-ids.c: -------------------------------------------------------------------------------- 1 | // 8-ids.c 2 | 3 | #include 4 | #include 5 | 6 | int main(void) { 7 | 8 | printf("Parent pid = %d\n", getppid()); 9 | 10 | printf("Uid = %d\n", getuid()); 11 | 12 | printf("Euid = %d\n", geteuid()); 13 | 14 | printf("Gid = %d\n", getgid()); 15 | 16 | printf("Egid = %d\n", getegid()); 17 | } 18 | -------------------------------------------------------------------------------- /os/apue/code/8-parent-wait-for-child.c: -------------------------------------------------------------------------------- 1 | // 8-parent-wait-for-child.c 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | int main(void) { 8 | 9 | pid_t pid = fork(); 10 | 11 | if (pid == 0) { 12 | printf("child return\n"); 13 | } 14 | else if (pid > 0) { 15 | wait(NULL); 16 | printf("parent return\n"); 17 | } 18 | else { 19 | printf("fork error\n"); 20 | return 1; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /os/apue/code/8-share-file-text: -------------------------------------------------------------------------------- 1 | child write 2 | parent write 3 | -------------------------------------------------------------------------------- /os/apue/code/8-share-file.c: -------------------------------------------------------------------------------- 1 | // 8-share-file.c 2 | 3 | #include 4 | #include 5 | 6 | #define PARENT_CONENT "parent write\n" 7 | #define CHILD_CONTENT "child write\n" 8 | 9 | int main(void) { 10 | 11 | // file 12 | 13 | FILE *f = fopen("8-share-file-text", "wa"); 14 | 15 | if (f == NULL) { 16 | printf("open/create file fail\n"); 17 | return 1; 18 | } 19 | 20 | // process 21 | 22 | pid_t pid = fork(); 23 | 24 | if (pid == 0) { 25 | // child 26 | fputs(CHILD_CONTENT, f); 27 | } 28 | else if (pid > 0) { 29 | // parent 30 | fputs(PARENT_CONENT, f); 31 | } 32 | else { 33 | printf("fork fail\n"); 34 | return 2; 35 | } 36 | 37 | return 0; 38 | } 39 | -------------------------------------------------------------------------------- /os/apue/code/8-vfork.c: -------------------------------------------------------------------------------- 1 | // 8-vfork.c 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | int main(void) { 9 | 10 | pid_t pid = vfork(); 11 | 12 | if (pid == 0) { 13 | printf("child running\n"); 14 | exit(0); 15 | // signal parent to continue ... 16 | } 17 | else if (pid > 0) { 18 | printf("parent running\n"); 19 | } 20 | else { 21 | printf("vfork error\n"); 22 | return 1; 23 | } 24 | 25 | return 0; 26 | } 27 | -------------------------------------------------------------------------------- /os/apue/code/hello_world.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int main(void) { 4 | printf("hello world\n"); 5 | return 0; 6 | } 7 | -------------------------------------------------------------------------------- /os/apue/code/test-3-close: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huangzworks/note/82ce594784a1f31095fc7a40cc0ab11e863c5db4/os/apue/code/test-3-close -------------------------------------------------------------------------------- /os/apue/code/test-3-creat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huangzworks/note/82ce594784a1f31095fc7a40cc0ab11e863c5db4/os/apue/code/test-3-creat -------------------------------------------------------------------------------- /os/apue/code/test-3-hole: -------------------------------------------------------------------------------- 1 | abcdefghello moto -------------------------------------------------------------------------------- /os/apue/code/test-3-lseek: -------------------------------------------------------------------------------- 1 | hello moto -------------------------------------------------------------------------------- /os/apue/code/test-3-open-create-file.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huangzworks/note/82ce594784a1f31095fc7a40cc0ab11e863c5db4/os/apue/code/test-3-open-create-file.txt -------------------------------------------------------------------------------- /os/apue/code/test-3-open-create-with-excl.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huangzworks/note/82ce594784a1f31095fc7a40cc0ab11e863c5db4/os/apue/code/test-3-open-create-with-excl.txt -------------------------------------------------------------------------------- /os/apue/code/test-3-print-file-descriptor: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huangzworks/note/82ce594784a1f31095fc7a40cc0ab11e863c5db4/os/apue/code/test-3-print-file-descriptor -------------------------------------------------------------------------------- /os/apue/code/test-3-read: -------------------------------------------------------------------------------- 1 | hello moto -------------------------------------------------------------------------------- /os/apue/code/test-3-write: -------------------------------------------------------------------------------- 1 | hello moto 2 | -------------------------------------------------------------------------------- /os/apue/code/test-4-truncate: -------------------------------------------------------------------------------- 1 | // 4-truncate.c 2 | 3 | #include // truncate 4 | #include // stderr, fprintf, perror 5 | #inc -------------------------------------------------------------------------------- /os/apue/image/4-12.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huangzworks/note/82ce594784a1f31095fc7a40cc0ab11e863c5db4/os/apue/image/4-12.png -------------------------------------------------------------------------------- /os/apue/image/8-share-file.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huangzworks/note/82ce594784a1f31095fc7a40cc0ab11e863c5db4/os/apue/image/8-share-file.png -------------------------------------------------------------------------------- /os/apue/image/share-file-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huangzworks/note/82ce594784a1f31095fc7a40cc0ab11e863c5db4/os/apue/image/share-file-1.png -------------------------------------------------------------------------------- /os/apue/image/share-file-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huangzworks/note/82ce594784a1f31095fc7a40cc0ab11e863c5db4/os/apue/image/share-file-2.png -------------------------------------------------------------------------------- /os/apue/image/share-file-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huangzworks/note/82ce594784a1f31095fc7a40cc0ab11e863c5db4/os/apue/image/share-file-3.png -------------------------------------------------------------------------------- /os/apue/index.rst: -------------------------------------------------------------------------------- 1 | 《UNIX 环境高级编程》笔记 2 | ============================== 3 | 4 | .. toctree:: 5 | :maxdepth: 2 6 | 7 | use-code 8 | chp3 9 | chp4 10 | chp8 11 | chp10 12 | chp11 13 | chp14 14 | -------------------------------------------------------------------------------- /os/apue/use-code.rst: -------------------------------------------------------------------------------- 1 | 源码的编译和使用 2 | ========================= 3 | 4 | APUE 的源码本身带有一些错误,需要修改之后才能正常编译。 5 | 6 | 以下方法在 Ubuntu 12.10 上测试通过。 7 | 8 | 9 | 步骤 10 | ------ 11 | 12 | 1. 到 www.apuebook.com 下载源码 13 | 14 | 2. ``tar`` 解包, ``cd apue.2e`` 15 | 16 | 3. 编辑 ``Make.defines.linux`` ,修改变量 ``WKDIR`` ,指向你放置 apue 源码的位置,我的是 ``/home/huangz/code/apue.2e`` ,所以设置为 ``WKDIR=/home/huangz/code/apue.2e`` 17 | 18 | 4. 编辑 ``include/apue.h`` ,增加一个常量 ``ARG_MAX`` : ``#defines ARG_MAX 4096`` , ``threadctl/getenv1.c`` 和 ``threadctl/getenv3.c`` 要用到这个常量; ``4096`` 只是参考值,可以视情况按需修改。 19 | 20 | 5. 编辑 ``threadctl/getenv1.c`` 增加一行: ``#include "apue.h"`` 21 | 22 | 6. 编辑 ``threadctl/getenv3.c`` 增加一行: ``#include "apue.h"`` 23 | 24 | 7. 编辑 ``threads/badexit2.c`` ,修改第 31 行,将 ``pthread_self()`` 的返回值转换为 ``int`` 类型: ``printf("thread 2: ID is %d\n", (int)pthread_self());`` 。 25 | 26 | 8. 编辑 ``std/linux.mk`` ,将两个 ``nawk`` 替换为 ``gawk`` 。 27 | 28 | 9. ``make`` 29 | 30 | 10. 将相应的文件复制到库位置里: ``sudo cp include/apue.h /usr/include`` ,以及 ``sudo cp lib/libapue.a /usr/lib`` 。 31 | 32 | 至此,所有步骤执行完成了。别忘了在编译的时候,要让编辑器链接 ``apue`` 库: 33 | 34 | :: 35 | 36 | gcc xxxx.c -lapue 37 | 38 | 39 | 参考资料 40 | ----------- 41 | 42 | http://hi.baidu.com/crazyboymx/blog/item/d3520bde28d79172d0164eb9.html 43 | -------------------------------------------------------------------------------- /os/fedora/index.rst: -------------------------------------------------------------------------------- 1 | 《Fedora 系统管理员指南》笔记 2 | ============================================ 3 | 4 | 原文来自: http://docs.fedoraproject.org/en-US/Fedora/21/html/System_Administrators_Guide/index.html 5 | 6 | 包管理: 7 | 8 | .. toctree:: 9 | 10 | yum 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /storage/mariadb/index.rst: -------------------------------------------------------------------------------- 1 | MariaDB 2 | ============= 3 | 4 | 参考资料: 5 | 6 | - MariaDB Knowledge Base : https://mariadb.com/kb/en/ 7 | 8 | - MySQL (5.6)文档: http://dev.mysql.com/doc/refman/5.6/en/index.html 9 | 10 | 目录: 11 | 12 | .. toctree:: 13 | :maxdepth: 2 14 | 15 | version 16 | install 17 | send-query 18 | primary-db-operation 19 | primary-table-operation 20 | primary-query-operation 21 | -------------------------------------------------------------------------------- /storage/mariadb/install.rst: -------------------------------------------------------------------------------- 1 | 安装 2 | =========== 3 | 4 | 访问 MariaDB 网站上的 Repository Configuration Tool ,根据你的系统配置选择最合适的安装方式: https://downloads.mariadb.org/mariadb/repositories 。 5 | 6 | 如果一切正常的话,那么安装成功之后应该可以看到 MariaDB 的版本号: 7 | 8 | :: 9 | 10 | $ mysql --version 11 | mysql Ver 15.1 Distrib 10.0.9-MariaDB, for debian-linux-gnu (x86_64) using readline 5.1 12 | 13 | .. note:: 为了兼容性考虑,在很多系统上面,安装好的 MariaDB 都继续沿用 ``mysql`` 作为程序名。 14 | 15 | 启动 MariaDB 试试: 16 | 17 | :: 18 | 19 | $ mysql -u root -p 20 | Enter password: 21 | Welcome to the MariaDB monitor. Commands end with ; or \g. 22 | Your MariaDB connection id is 34 23 | Server version: 10.0.9-MariaDB-1~wheezy-log mariadb.org binary distribution 24 | 25 | Copyright (c) 2000, 2014, Oracle, Monty Program Ab and others. 26 | 27 | Type 'help;' or '\h' for help. Type '\c' to clear the current input statement. 28 | 29 | MariaDB [(none)]> 30 | 31 | -------------------------------------------------------------------------------- /storage/mariadb/primary-db-operation.rst: -------------------------------------------------------------------------------- 1 | 基本数据库操作 2 | =================== 3 | 4 | 5 | 查看数据库 6 | -------------- 7 | 8 | 使用 ``SHOT DATABASES`` 命令来查看服务器现有的数据库: 9 | 10 | :: 11 | 12 | MariaDB [(none)]> SHOW DATABASES; 13 | +--------------------+ 14 | | Database | 15 | +--------------------+ 16 | | information_schema | 17 | | mysql | 18 | | performance_schema | 19 | +--------------------+ 20 | 3 rows in set (0.02 sec) 21 | 22 | 23 | 使用数据库 24 | ------------- 25 | 26 | 使用 ``USE `` 命令可以将目标数据库设置为操作对象: 27 | 28 | :: 29 | 30 | MariaDB [(none)]> USE mysql 31 | Reading table information for completion of table and column names 32 | You can turn off this feature to get a quicker startup with -A 33 | 34 | Database changed 35 | MariaDB [mysql]> 36 | 37 | 注意 ``USE`` 是一条特殊的命令: 38 | 39 | - 它不需要分号结尾,不过如果你喜欢的话,加上分号也是无害的; 40 | 41 | - 它必须在一行之内结束,不能换行。 42 | 43 | 另外要注意的是客户端的提示符, 44 | 在没有指定数据库的时候, 45 | 提示符显示的是 ``MariaDB [(none)]>`` , 46 | 而当我们指定了 ``mysql`` 数据库之后, 47 | 提示符变成了 ``MariaDB [mysql]>`` 。 48 | 49 | 50 | 创建数据库 51 | ----------------- 52 | 53 | 使用命令 ``CRATE DATABASE `` 可以创建一个新的数据库: 54 | 55 | :: 56 | 57 | MariaDB [(none)]> CREATE DATABASE menagerie; 58 | Query OK, 1 row affected (0.00 sec) 59 | 60 | MariaDB [(none)]> SHOW DATABASES; 61 | +--------------------+ 62 | | Database | 63 | +--------------------+ 64 | | information_schema | 65 | | menagerie | # <--- 新创建的数据库 66 | | mysql | 67 | | performance_schema | 68 | +--------------------+ 69 | 4 rows in set (0.00 sec) 70 | 71 | 创建一个新的数据库并不会自动将这个新的数据库设置为目标数据库, 72 | 所以要使用这个新数据库的话, 73 | 还需要再执行一条 ``USE menagerie`` 命令。 74 | 75 | 另外, 76 | 在启动 MariaDB 的时候, 77 | 也可以使用: 78 | 79 | :: 80 | 81 | mysql -h -u -p 82 | 83 | 的方式在启动客户端时指定数据库, 84 | 这和启动客户端之后再执行 ``USE `` 的效果一样。 85 | 86 | 87 | 删除数据库 88 | --------------- 89 | 90 | TODO 91 | -------------------------------------------------------------------------------- /storage/mariadb/primary-query-operation.rst: -------------------------------------------------------------------------------- 1 | 基本查询操作 2 | ================ 3 | 4 | TODO:介绍针对数据库表的 CRUD 操作。 5 | -------------------------------------------------------------------------------- /storage/mariadb/primary-table-operation.rst: -------------------------------------------------------------------------------- 1 | 基本表格操作 2 | =============== 3 | 4 | 创建表格、删除表格、修改表格、表格包含的行和列以及类型,etc。 5 | 6 | http://dev.mysql.com/doc/refman/5.7/en/create-table.html 7 | -------------------------------------------------------------------------------- /storage/mariadb/send-query.rst: -------------------------------------------------------------------------------- 1 | 发送查询 2 | ============== 3 | 4 | 查询数据库的当前版本: 5 | 6 | :: 7 | 8 | MariaDB [(none)]> SELECT VERSION(); 9 | +-----------------------------+ 10 | | VERSION() | 11 | +-----------------------------+ 12 | | 10.0.9-MariaDB-1~wheezy-log | 13 | +-----------------------------+ 14 | 1 row in set (0.01 sec) 15 | 16 | 因为关键字\ *不限制大小写*\ ,所以以下语句和上面的语句产生同样的效果: 17 | 18 | :: 19 | 20 | SELECT VERSION(); 21 | select version(); 22 | SeLeCt VeRsIoN(); 23 | 24 | 还可以在一行里面输入多个查询, 25 | 只要使用分号(\ ``;``\ )来隔开各个查询就可以了: 26 | 27 | :: 28 | 29 | MariaDB [(none)]> SELECT VERSION(); SELECT NOW(); 30 | +-----------------------------+ 31 | | VERSION() | 32 | +-----------------------------+ 33 | | 10.0.9-MariaDB-1~wheezy-log | 34 | +-----------------------------+ 35 | 1 row in set (0.00 sec) 36 | 37 | +---------------------+ 38 | | NOW() | 39 | +---------------------+ 40 | | 2014-03-14 21:40:22 | 41 | +---------------------+ 42 | 1 row in set (0.00 sec) 43 | 44 | 因为客户端根据分号而不是行数来判断查询是否完整, 45 | 所以我们可以将一个查询分到多个行里面: 46 | 47 | :: 48 | 49 | MariaDB [(none)]> SELECT 50 | -> USER() 51 | -> ; 52 | +----------------+ 53 | | USER() | 54 | +----------------+ 55 | | root@localhost | 56 | +----------------+ 57 | 1 row in set (0.00 sec) 58 | 59 | 注意, 60 | 在将一个查询分到多个行里面写入时, 61 | 客户端的提示符从原来的 ``>`` 变成了 ``->`` , 62 | 这表示客户端正在等待后续的输入。 63 | 64 | 下表展示了一些可能在客户端里面出现的提示符, 65 | 以及这些提示符的意义: 66 | 67 | ========= ================================================================================================= 68 | 提示符 意义 69 | ========= ================================================================================================= 70 | ``>`` 等待新查询的输入。 71 | ``->`` 等待新的多行输入。 72 | ``'>`` 等待新的多行输入,以及一个以 ``'`` 开始的字符串的结尾。 73 | ``">`` 等待新的多行输入,以及一个以 ``"`` 开始的字符串的结尾。 74 | ``\`>`` 等待新的多行输入,以及一个以 ``\``` 符号开始的标识符的结尾。 75 | ``/*>`` 等待新的多行输入,以及一个以 ``/*`` 开始的注释的结尾。 76 | ========= ================================================================================================= 77 | 78 | 如果在输入查询时键入了错误的内容, 79 | 那么可以输入 ``\c`` 来撤销已键入的查询: 80 | 81 | :: 82 | 83 | MariaDB [(none)]> SELECT 84 | -> WRONG INPUT 85 | -> OOPS 86 | -> \c 87 | 88 | -------------------------------------------------------------------------------- /storage/mariadb/version.rst: -------------------------------------------------------------------------------- 1 | 版本号说明 2 | ============== 3 | 4 | MariaDB 现在主要有 5.5 和 10.0 两个系列版本, 5 | 它们的主要区别是: 6 | 7 | - MariaDB 5.5 基本上是对 MySQL 5.5 版本的开源 fork , 8 | 在兼容 MySQL 5.5 特性的基础上, 9 | MariaDB 进行了一些优化, 10 | 并添加了一些新特性。 11 | 12 | - MariaDB 10.0 基于 MariaDB 5.5 的基础上进行开发, 13 | 它的目标是实现大部分(而不是所有) MySQL 5.6 的特性, 14 | 并添加一些 MariaDB 10.0 独有的新特性。 15 | 16 | MariaDB 5.5 是当前的稳定版(stable,GA)释出, 17 | 而 MariaDB 10.0 则是当前的开发版释出: 18 | 19 | - 在实际使用方面,推荐使用 MariaDB 5.5 。 20 | 21 | - 如果是学习或者测试的话,推荐使用 MariaDB 10.0 ,因为有更多新特性可玩。 22 | 23 | 因为本书关注的主要是如何学习 MariaDB , 24 | 所以本书使用 MariaDB 10.0 版本进行演示。 25 | -------------------------------------------------------------------------------- /storage/redis_code_analysis/adlist.rst: -------------------------------------------------------------------------------- 1 | .. highlight:: c 2 | 3 | 双链表 4 | ================= 5 | 6 | 7 | 链表是 Redis 的核心数据结构之一,它不仅大量应用在 Redis 自身内部的实现中,而且它也是 Redis 的 List 结构的底层实现之一。 8 | 9 | 本文通过分析 Redis 源码里的 ``adlist.h`` 和 ``adlist.c`` ,了解链表结构的详细实现,籍此加深对 Redis 的理解。 10 | 11 | 12 | 数据结构 13 | --------------- 14 | 15 | Redis 的链表结构是一个典型的双端链表 `doubly linked list `_ 实现。 16 | 17 | 除了一个指向值的 ``void`` 指针外,链表中的每个节点都有两个方向指针,一个指向前驱节点,另一个指向后继节点: 18 | 19 | :: 20 | 21 | typedef struct listNode { 22 | struct listNode *prev; 23 | struct listNode *next; 24 | void *value; 25 | } listNode; 26 | 27 | 28 | 每个双端链表都被一个 ``list`` 结构包装起来, ``list`` 结构带有两个指针,一个指向双端链表的表头节点,另一个指向双端链表的表尾节点,这个特性使得 Redis 可以很方便地执行像 `RPOPLPUSH `_ 这样的命令: 29 | 30 | :: 31 | 32 | typedef struct list { 33 | listNode *head; 34 | listNode *tail; 35 | 36 | void *(*dup)(void *ptr); 37 | void (*free)(void *ptr); 38 | int (*match)(void *ptr, void *key); 39 | 40 | unsigned long len; 41 | } list; 42 | 43 | 链表结构中还有三个函数指针 ``dup`` 、 ``free`` 和 ``match`` ,这些指针指向那些用于处理不同类型值的函数。 44 | 45 | 至于 ``len`` 属性,毫无疑问,就是链表节点数量计数器了。 46 | 47 | 以下是双端链表和节点的一个示意图: 48 | 49 | .. image:: adlist/list_and_list_node.png 50 | 51 | 52 | list 结构和 listNode 结构的 API 53 | -------------------------------------- 54 | 55 | ``list`` 和 ``listNode`` 都有它们自己的一簇 API ,这些 API 的实现都是典型的双端链表 API ,这里就不作详细的分析了。 56 | 57 | 从名字上就可以大概地看出它们的作用: 58 | 59 | :: 60 | 61 | list *listCreate(void); 62 | void listRelease(list *list); 63 | 64 | list *listAddNodeHead(list *list, void *value); 65 | list *listAddNodeTail(list *list, void *value); 66 | list *listInsertNode(list *list, listNode *old_node, void *value, int after); 67 | void listDelNode(list *list, listNode *node); 68 | 69 | list *listDup(list *orig); 70 | 71 | listNode *listSearchKey(list *list, void *key); 72 | listNode *listIndex(list *list, long index); 73 | 74 | void listRotate(list *list); 75 | 76 | 77 | 为了方便操作列表,源码中还定义了一组宏: 78 | 79 | :: 80 | 81 | #define listLength(l) ((l)->len) 82 | #define listFirst(l) ((l)->head) 83 | #define listLast(l) ((l)->tail) 84 | #define listPrevNode(n) ((n)->prev) 85 | #define listNextNode(n) ((n)->next) 86 | #define listNodeValue(n) ((n)->value) 87 | 88 | #define listSetDupMethod(l,m) ((l)->dup = (m)) 89 | #define listSetFreeMethod(l,m) ((l)->free = (m)) 90 | #define listSetMatchMethod(l,m) ((l)->match = (m)) 91 | 92 | #define listGetDupMethod(l) ((l)->dup) 93 | #define listGetFree(l) ((l)->free) 94 | #define listGetMatchMethod(l) ((l)->match) 95 | 96 | 97 | 迭代器 98 | ----------- 99 | 100 | Redis 针对 ``list`` 结构实现了一个\ `迭代器 `_ ,用于对链表进行遍历。 101 | 102 | 这个迭代器的实现非常典型,它的结构定义如下: 103 | 104 | :: 105 | 106 | typedef struct listIter { 107 | listNode *next; 108 | int direction; // 指定迭代的方向(从前到后还是从后到前) 109 | } listIter; 110 | 111 | ``direction`` 决定迭代器是沿着 ``next`` 指针向后迭代,还是沿着 ``prev`` 指针向前迭代,这个值可以是 ``adlist.h`` 中的 ``AL_START_HEAD`` 常量或 ``AL_START_TAIL`` 常量: 112 | 113 | :: 114 | 115 | #define AL_START_HEAD 0 116 | #define AL_START_TAIL 1 117 | 118 | 以下是迭代器所使用的 API : 119 | 120 | :: 121 | 122 | listIter *listGetIterator(list *list, int direction); 123 | listNode *listNext(listIter *iter); 124 | 125 | void listReleaseIterator(listIter *iter); 126 | 127 | void listRewind(list *list, listIter *li); 128 | void listRewindTail(list *list, listIter *li); 129 | 130 | 131 | 小结 132 | ----- 133 | 134 | 和以往不同,因为双端链表和链表迭代器都非常常见,所以这篇文章没有像往常一样,对实现源码作详细的分析,而是将注意力集中到数据结构的定义,以及 API 的展示上。 135 | 136 | 如果对源码的细节感兴趣,可以到 GitHub 上查看带注释的完整源码: `https://github.com/huangz1990/reading_redis_source `_ 。 137 | -------------------------------------------------------------------------------- /storage/redis_code_analysis/adlist/list_and_list_node.dot: -------------------------------------------------------------------------------- 1 | digraph list_and_list_node { 2 | rankdir=LR; 3 | node [shape=record]; 4 | label = "双端链表与节点"; 5 | 6 | list_node_1 [label = "{ prev| value| next}"]; 7 | list_node_2 [label = "{ prev| value| next}"]; 8 | list_node_3 [label = "{ prev| value| next}"]; 9 | 10 | node [width=1.5]; 11 | list [label = " head| tail| dup| free| match| len"]; 12 | list_node_1 -> list_node_2; 13 | list_node_2 -> list_node_1; 14 | 15 | list_node_2 -> list_node_3; 16 | list_node_3 -> list_node_2; 17 | 18 | list:head -> list_node_1; 19 | list:tail -> list_node_3; 20 | } 21 | -------------------------------------------------------------------------------- /storage/redis_code_analysis/adlist/list_and_list_node.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huangzworks/note/82ce594784a1f31095fc7a40cc0ab11e863c5db4/storage/redis_code_analysis/adlist/list_and_list_node.png -------------------------------------------------------------------------------- /storage/redis_code_analysis/cluster.rst: -------------------------------------------------------------------------------- 1 | .. highlight:: c 2 | 3 | 集群(cluster) 4 | ===================== 5 | 6 | 本次注释包含 ``cluster.h`` 文件和 ``cluster.c`` 文件, 7 | 源文件来自 Redis 项目 ``unstable`` 分支的最新文件, 8 | 最后一次提交来自: 9 | 10 | :: 11 | 12 | commit e78938425536748e63932ccebb7248f6389db102 13 | Author: antirez 14 | Date: Mon Dec 23 12:48:39 2013 +0100 15 | 16 | 两个注释文件的代码都可以在 https://github.com/huangz1990/blog/blob/master/storage/redis_code_analysis/cluster 找到。 17 | 18 | cluster.h 19 | ^^^^^^^^^^^^^^^^ 20 | 21 | .. literalinclude:: cluster/cluster.h 22 | 23 | cluster.c 24 | ^^^^^^^^^^^^^^^^^ 25 | 26 | .. literalinclude:: cluster/cluster.c 27 | -------------------------------------------------------------------------------- /storage/redis_code_analysis/dict/add-element.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huangzworks/note/82ce594784a1f31095fc7a40cc0ab11e863c5db4/storage/redis_code_analysis/dict/add-element.png -------------------------------------------------------------------------------- /storage/redis_code_analysis/dict/create-dict.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huangzworks/note/82ce594784a1f31095fc7a40cc0ab11e863c5db4/storage/redis_code_analysis/dict/create-dict.png -------------------------------------------------------------------------------- /storage/redis_code_analysis/dict/incremental-rehashing-functions.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huangzworks/note/82ce594784a1f31095fc7a40cc0ab11e863c5db4/storage/redis_code_analysis/dict/incremental-rehashing-functions.png -------------------------------------------------------------------------------- /storage/redis_code_analysis/dict/relationship.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huangzworks/note/82ce594784a1f31095fc7a40cc0ab11e863c5db4/storage/redis_code_analysis/dict/relationship.png -------------------------------------------------------------------------------- /storage/redis_code_analysis/event-driven/event-loop.dot: -------------------------------------------------------------------------------- 1 | digraph event_loop { 2 | start [label="aeMain()\n开始循环"]; 3 | check_stop [label="如果 eventLoop->stop 不为真\n那么继续执行事件处理"]; 4 | check_beforesleep [label="如果 eventLoop->beforesleep 不为空\n那么执行它"]; 5 | ae_process_events [label="aeProcessEvents\n开始处理事件"]; 6 | 7 | start -> check_stop; 8 | check_stop -> check_beforesleep; 9 | check_beforesleep -> ae_process_events; 10 | ae_process_events -> check_stop; 11 | } 12 | -------------------------------------------------------------------------------- /storage/redis_code_analysis/event-driven/event-loop.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huangzworks/note/82ce594784a1f31095fc7a40cc0ab11e863c5db4/storage/redis_code_analysis/event-driven/event-loop.png -------------------------------------------------------------------------------- /storage/redis_code_analysis/index.rst: -------------------------------------------------------------------------------- 1 | .. Right Track Wrong Train documentation master file, created by 2 | sphinx-quickstart on Sat Oct 6 22:07:15 2012. 3 | You can adapt this file completely to your liking, but it should at least 4 | contain the root `toctree` directive. 5 | 6 | Redis 源码分析 7 | =================================================== 8 | 9 | .. toctree:: 10 | :maxdepth: 2 11 | 12 | adlist 13 | dict 14 | sds 15 | 16 | lua-scripting 17 | event-driven 18 | pubsub 19 | transaction 20 | watch-and-unwatch 21 | keyspace-notification 22 | pubsub_command 23 | replication 24 | sentinel 25 | cluster 26 | -------------------------------------------------------------------------------- /storage/redis_code_analysis/pubsub/list_and_list_node.dot: -------------------------------------------------------------------------------- 1 | digraph list_and_list_node { 2 | rankdir=LR; 3 | node [shape=record]; 4 | label = "双端链表与节点"; 5 | 6 | list_node_1 [label = "{ prev| value| next}"]; 7 | list_node_2 [label = "{ prev| value| next}"]; 8 | list_node_3 [label = "{ prev| value| next}"]; 9 | 10 | node [width=1.5]; 11 | list [label = " head| tail| dup| free| match| len"]; 12 | list_node_1 -> list_node_2; 13 | list_node_2 -> list_node_1; 14 | 15 | list_node_2 -> list_node_3; 16 | list_node_3 -> list_node_2; 17 | 18 | list:head -> list_node_1; 19 | list:tail -> list_node_3; 20 | } 21 | -------------------------------------------------------------------------------- /storage/redis_code_analysis/pubsub/list_and_list_node.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huangzworks/note/82ce594784a1f31095fc7a40cc0ab11e863c5db4/storage/redis_code_analysis/pubsub/list_and_list_node.png -------------------------------------------------------------------------------- /storage/redis_code_analysis/pubsub/psubscribe.dot: -------------------------------------------------------------------------------- 1 | digraph psubscribe { 2 | 3 | rankdir=LR; 4 | node [shape="record"]; 5 | 6 | redisServer [label="redisServer\ 结构|其他属性\ ...|pubsub_patterns|其他属性\ ..."]; 7 | 8 | pattern_1 [label="client:\ client_789|news.*"]; 9 | pattern_2 [label="client:\ client_999|news.*"]; 10 | pattern_1 [label="pattern:\ news.*|client:\ client_789"]; 11 | pattern_2 [label="pattern:\ news.*|client:\ client_999"]; 12 | 13 | redisServer:pubsub_patterns -> pattern_1 [label="所有模式"]; 14 | 15 | pattern_1 -> pattern_2; 16 | pattern_2 -> pattern_1; 17 | 18 | } 19 | -------------------------------------------------------------------------------- /storage/redis_code_analysis/pubsub/psubscribe.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huangzworks/note/82ce594784a1f31095fc7a40cc0ab11e863c5db4/storage/redis_code_analysis/pubsub/psubscribe.png -------------------------------------------------------------------------------- /storage/redis_code_analysis/pubsub/pubsub.dot: -------------------------------------------------------------------------------- 1 | digraph pubsub { 2 | rankdir=LR; 3 | channel [shape="box"]; 4 | publisher -> channel [label="channel\nmsg"]; 5 | channel -> subscriber_1 [label="msg"]; 6 | channel -> subscriber_2 [label="msg"]; 7 | channel -> subscriber_3 [label="msg"]; 8 | } 9 | -------------------------------------------------------------------------------- /storage/redis_code_analysis/pubsub/pubsub.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huangzworks/note/82ce594784a1f31095fc7a40cc0ab11e863c5db4/storage/redis_code_analysis/pubsub/pubsub.png -------------------------------------------------------------------------------- /storage/redis_code_analysis/pubsub/subscribe.dot: -------------------------------------------------------------------------------- 1 | digraph subscribe { 2 | rankdir=LR; 3 | node [shape="record"]; 4 | 5 | redisServer [label="redisServer\ 结构|其他属性\ ...| pubsub_channels|其他属性\ ..."]; 6 | 7 | pubsub_channels [label="其他频道\ ...| news|其他频道\ ..."]; 8 | 9 | client_123 [label="client_123"]; 10 | client_456 [label="client_456"]; 11 | 12 | redisServer:pubsub_channels -> pubsub_channels [label="所有频道"]; 13 | pubsub_channels:news -> client_123 [label="所有订阅客户端"]; 14 | client_123 -> client_456; 15 | client_456 -> client_123; 16 | } 17 | -------------------------------------------------------------------------------- /storage/redis_code_analysis/pubsub/subscribe.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huangzworks/note/82ce594784a1f31095fc7a40cc0ab11e863c5db4/storage/redis_code_analysis/pubsub/subscribe.png -------------------------------------------------------------------------------- /storage/redis_code_analysis/sds/sdshdr.dot: -------------------------------------------------------------------------------- 1 | digraph sdshdr { 2 | rankdir = LR; 3 | start_of_sdshdr [label="sds - sizeof(struct sdshdr)", shape=plaintext]; 4 | start_of_buf [label="sds", shape=plaintext]; 5 | 6 | node[shape=record]; 7 | sdshdr [label="struct sdshdr|{len|free|buf}"]; 8 | 9 | start_of_sdshdr -> sdshdr; 10 | start_of_buf -> sdshdr:start_of_buf; 11 | } 12 | -------------------------------------------------------------------------------- /storage/redis_code_analysis/sds/sdshdr.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huangzworks/note/82ce594784a1f31095fc7a40cc0ab11e863c5db4/storage/redis_code_analysis/sds/sdshdr.png -------------------------------------------------------------------------------- /storage/redis_code_analysis/sentinel.rst: -------------------------------------------------------------------------------- 1 | .. highlight:: c 2 | 3 | Sentinel 4 | ================= 5 | 6 | 以下是 Redis 2.8 新版 Sentinel 的注释代码, 7 | 源文件在\ `这里 `_\ : 8 | 9 | .. literalinclude:: sentinel/sentinel.c 10 | -------------------------------------------------------------------------------- /storage/reuse-redis-module/adlist.rst: -------------------------------------------------------------------------------- 1 | .. highlight:: c 2 | 3 | 双端链表 4 | ============ 5 | 6 | 7 | 适用范围 8 | -------------------- 9 | 10 | Redis 实现的是一个典型的双端链表, 11 | 这个链表具有以下特性: 12 | 13 | 1. 带有表头和表尾指针,可以在 :math:`O(1)` 复杂度内取得表头或者表尾节点 14 | 15 | 2. 带有节点数量变量,可以在 :math:`O(1)` 复杂度内取得链表的节点数量 16 | 17 | 3. 可以通过指定 ``dup`` 、 ``free`` 和 ``match`` 函数,适应多种类型的值(或结构) 18 | 19 | 4. 带有一个链表迭代器,通过这个迭代器,可以从表头向表尾或者从表尾向表头进行迭代。 20 | 21 | 22 | 准备工作 23 | -------------------- 24 | 25 | 1. 从 Redis 源码中复制 ``adlist.c`` 、 ``adlist.h`` 、 ``zmalloc.c`` 和 ``zmalloc.h`` 四个文件到目标文件夹。 26 | 27 | 2. 将 ``zmalloc.c`` 中的 ``#include "config.h"`` 一行删掉。 28 | 29 | 3. 添加 ``#include `` 到 ``zamlloc.h`` ,解决 ``size_t`` 未定义的问题。 30 | 31 | 32 | 测试驱动程序 33 | ------------------- 34 | 35 | 以下是用作节点值的空对象: 36 | 37 | .. literalinclude:: code/adlist/object.h 38 | 39 | .. literalinclude:: code/adlist/object.c 40 | 41 | 以下程序进行了三项测试: 42 | 43 | 1. 创建一个空双端链表,并检查它的各项属性,然后释放它 44 | 45 | 2. 创建一个非空双端链表,然后用函数(宏)迭代节点,并检查它们的值 46 | 47 | 3. 创建一个非空双端链表,然后用迭代器对列表进行迭代,并检查各个节点的值 48 | 49 | .. literalinclude:: code/adlist/main.c 50 | 51 | 52 | 完整源码 53 | ------------------- 54 | 55 | 测试程序的完整源码可以在 `这里 `_ 的 ``adlist`` 文件夹下找到。 56 | -------------------------------------------------------------------------------- /storage/reuse-redis-module/code/adlist/main.c: -------------------------------------------------------------------------------- 1 | // main.c 2 | 3 | #include 4 | #include 5 | 6 | #include "adlist.h" 7 | #include "object.h" 8 | 9 | void test_empty_list(void) 10 | { 11 | // 创建一个新链表 12 | list* l = listCreate(); 13 | 14 | // 检查表头和表尾 15 | assert( 16 | l->head == NULL && 17 | l->tail == NULL 18 | ); 19 | 20 | // 检查节点数量 21 | assert( 22 | l->len == 0 23 | ); 24 | 25 | // 检查类型限定函数 26 | assert( 27 | l->dup == NULL && 28 | l->free == NULL && 29 | l->match == NULL 30 | ); 31 | 32 | // 释放链表 33 | listRelease(l); 34 | } 35 | 36 | 37 | void test_add_node_and_advance_by_pointer(void) 38 | { 39 | 40 | // 初始化值对象 41 | object *x = create_object(), 42 | *y = create_object(), 43 | *z = create_object(); 44 | 45 | 46 | // 创建列表 47 | list* l = listCreate(); 48 | // l = [x] 49 | listAddNodeHead(l, x); 50 | // l = [x, z] 51 | listAddNodeTail(l, z); 52 | // l = [x, y, z] 53 | listNode* node_contain_x = listSearchKey(l, x); 54 | listInsertNode(l, node_contain_x, y, 1); //insert y after x 55 | 56 | 57 | // 手动遍历节点 58 | listNode* current; 59 | 60 | // x 61 | current = listFirst(l); 62 | assert( 63 | current->value == x 64 | ); 65 | 66 | // y 67 | current = listNextNode(current); 68 | assert( 69 | current->value == y 70 | ); 71 | 72 | // z 73 | current = listNextNode(current); 74 | assert( 75 | current->value == z 76 | ); 77 | 78 | 79 | // 释放空间 80 | free_object(x); 81 | free_object(y); 82 | free_object(z); 83 | 84 | listRelease(l); 85 | } 86 | 87 | 88 | void test_iterator(void) 89 | { 90 | // 初始化值对象 91 | object *x = create_object(), 92 | *y = create_object(), 93 | *z = create_object(); 94 | 95 | 96 | // 创建列表 97 | list* l = listCreate(); 98 | listAddNodeTail(l, x); 99 | listAddNodeTail(l, y); 100 | listAddNodeTail(l, z); 101 | 102 | 103 | // 从表头向表尾遍历节点 104 | listIter* itertor = listGetIterator(l, AL_START_HEAD); 105 | listNode* current; 106 | 107 | // 第一个节点 108 | current = listNext(itertor); 109 | assert( 110 | current->value == x 111 | ); 112 | 113 | // 第二个节点 114 | current = listNext(itertor); 115 | assert( 116 | current->value == y 117 | ); 118 | 119 | // 第三个节点 120 | current = listNext(itertor); 121 | assert( 122 | current->value == z 123 | ); 124 | 125 | 126 | // 释放 127 | free_object(x); 128 | free_object(y); 129 | free_object(z); 130 | 131 | listRelease(l); 132 | 133 | listReleaseIterator(itertor); 134 | } 135 | 136 | void main(void) 137 | { 138 | test_empty_list(); 139 | 140 | test_add_node_and_advance_by_pointer(); 141 | 142 | test_iterator(); 143 | } 144 | -------------------------------------------------------------------------------- /storage/reuse-redis-module/code/adlist/object.c: -------------------------------------------------------------------------------- 1 | // object.c 2 | 3 | #include "zmalloc.h" 4 | #include "object.h" 5 | 6 | object* create_object(void) 7 | { 8 | return (object*)zmalloc(sizeof(object)); 9 | } 10 | 11 | void free_object(object* obj) 12 | { 13 | zfree(obj); 14 | } 15 | -------------------------------------------------------------------------------- /storage/reuse-redis-module/code/adlist/object.h: -------------------------------------------------------------------------------- 1 | // object.h 2 | 3 | typedef struct { } object; 4 | 5 | object* create_object(void); 6 | void free_object(object* object); 7 | -------------------------------------------------------------------------------- /storage/reuse-redis-module/code/adlist/zmalloc.h: -------------------------------------------------------------------------------- 1 | /* zmalloc - total amount of allocated memory aware version of malloc() 2 | * 3 | * Copyright (c) 2009-2010, Salvatore Sanfilippo 4 | * All rights reserved. 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions are met: 8 | * 9 | * * Redistributions of source code must retain the above copyright notice, 10 | * this list of conditions and the following disclaimer. 11 | * * Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * * Neither the name of Redis nor the names of its contributors may be used 15 | * to endorse or promote products derived from this software without 16 | * specific prior written permission. 17 | * 18 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 22 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 23 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 24 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 25 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 26 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 27 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 | * POSSIBILITY OF SUCH DAMAGE. 29 | */ 30 | #include 31 | 32 | #ifndef __ZMALLOC_H 33 | #define __ZMALLOC_H 34 | 35 | /* Double expansion needed for stringification of macro values. */ 36 | #define __xstr(s) __str(s) 37 | #define __str(s) #s 38 | 39 | #if defined(USE_TCMALLOC) 40 | #define ZMALLOC_LIB ("tcmalloc-" __xstr(TC_VERSION_MAJOR) "." __xstr(TC_VERSION_MINOR)) 41 | #include 42 | #if (TC_VERSION_MAJOR == 1 && TC_VERSION_MINOR >= 6) || (TC_VERSION_MAJOR > 1) 43 | #define HAVE_MALLOC_SIZE 1 44 | #define zmalloc_size(p) tc_malloc_size(p) 45 | #else 46 | #error "Newer version of tcmalloc required" 47 | #endif 48 | 49 | #elif defined(USE_JEMALLOC) 50 | #define ZMALLOC_LIB ("jemalloc-" __xstr(JEMALLOC_VERSION_MAJOR) "." __xstr(JEMALLOC_VERSION_MINOR) "." __xstr(JEMALLOC_VERSION_BUGFIX)) 51 | #include 52 | #if (JEMALLOC_VERSION_MAJOR == 2 && JEMALLOC_VERSION_MINOR >= 1) || (JEMALLOC_VERSION_MAJOR > 2) 53 | #define HAVE_MALLOC_SIZE 1 54 | #define zmalloc_size(p) je_malloc_usable_size(p) 55 | #else 56 | #error "Newer version of jemalloc required" 57 | #endif 58 | 59 | #elif defined(__APPLE__) 60 | #include 61 | #define HAVE_MALLOC_SIZE 1 62 | #define zmalloc_size(p) malloc_size(p) 63 | #endif 64 | 65 | #ifndef ZMALLOC_LIB 66 | #define ZMALLOC_LIB "libc" 67 | #endif 68 | 69 | void *zmalloc(size_t size); 70 | void *zcalloc(size_t size); 71 | void *zrealloc(void *ptr, size_t size); 72 | void zfree(void *ptr); 73 | char *zstrdup(const char *s); 74 | size_t zmalloc_used_memory(void); 75 | void zmalloc_enable_thread_safeness(void); 76 | void zmalloc_set_oom_handler(void (*oom_handler)(size_t)); 77 | float zmalloc_get_fragmentation_ratio(void); 78 | size_t zmalloc_get_rss(void); 79 | size_t zmalloc_get_private_dirty(void); 80 | void zlibc_free(void *ptr); 81 | 82 | #ifndef HAVE_MALLOC_SIZE 83 | size_t zmalloc_size(void *ptr); 84 | #endif 85 | 86 | #endif /* __ZMALLOC_H */ 87 | -------------------------------------------------------------------------------- /storage/reuse-redis-module/code/dict/fmacros.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2009-2012, Salvatore Sanfilippo 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * * Redistributions of source code must retain the above copyright notice, 9 | * this list of conditions and the following disclaimer. 10 | * * Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * * Neither the name of Redis nor the names of its contributors may be used 14 | * to endorse or promote products derived from this software without 15 | * specific prior written permission. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 21 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27 | * POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | 30 | #ifndef _REDIS_FMACRO_H 31 | #define _REDIS_FMACRO_H 32 | 33 | #define _BSD_SOURCE 34 | 35 | #if defined(__linux__) 36 | #define _GNU_SOURCE 37 | #endif 38 | 39 | #if defined(__linux__) || defined(__OpenBSD__) 40 | #define _XOPEN_SOURCE 700 41 | #else 42 | #define _XOPEN_SOURCE 43 | #endif 44 | 45 | #define _LARGEFILE_SOURCE 46 | #define _FILE_OFFSET_BITS 64 47 | 48 | #endif 49 | -------------------------------------------------------------------------------- /storage/reuse-redis-module/code/dict/int_dict_type.c: -------------------------------------------------------------------------------- 1 | // int_dict_type.c 2 | 3 | #include // NULL 4 | 5 | #include "zmalloc.h" 6 | #include "dict.h" 7 | #include "int_dict_type.h" 8 | 9 | 10 | /* 11 | * key 12 | */ 13 | KeyObject* create_key(void) 14 | { 15 | return (KeyObject*)zmalloc(sizeof(KeyObject)); 16 | } 17 | 18 | void release_key(KeyObject* key) 19 | { 20 | zfree(key); 21 | } 22 | 23 | 24 | /* 25 | * value 26 | */ 27 | ValueObject* create_value(void) 28 | { 29 | return (ValueObject*)zmalloc(sizeof(ValueObject)); 30 | } 31 | 32 | void release_value(ValueObject* value) 33 | { 34 | zfree(value); 35 | } 36 | 37 | 38 | /* 39 | * dict type 40 | */ 41 | unsigned int keyHashFunction(const void *key) 42 | { 43 | KeyObject *k = (KeyObject*)key; 44 | 45 | int value = k->value; 46 | 47 | if (value < 0) 48 | return 0 - value; 49 | else 50 | return value; 51 | } 52 | 53 | int keyCompareFunction(void *privdata, const void *x, const void *y) 54 | { 55 | DICT_NOTUSED(privdata); 56 | 57 | KeyObject *kx = (KeyObject*)x; 58 | KeyObject *ky = (KeyObject*)y; 59 | 60 | return (kx->value == ky->value); 61 | } 62 | 63 | void keyDestructor(void *privdata, void *key) 64 | { 65 | DICT_NOTUSED(privdata); 66 | 67 | release_key(key); 68 | } 69 | 70 | void valDestructor(void *privdata, void *value) 71 | { 72 | DICT_NOTUSED(privdata); 73 | 74 | release_value(value); 75 | } 76 | 77 | dictType intDictType = { 78 | keyHashFunction, 79 | NULL, 80 | NULL, 81 | keyCompareFunction, 82 | keyDestructor, 83 | valDestructor 84 | }; 85 | -------------------------------------------------------------------------------- /storage/reuse-redis-module/code/dict/int_dict_type.h: -------------------------------------------------------------------------------- 1 | // int_dict_type.h 2 | 3 | /* 4 | * key and value 5 | */ 6 | typedef struct { 7 | int value; 8 | } KeyObject; 9 | 10 | typedef struct { 11 | int value; 12 | } ValueObject; 13 | 14 | /* 15 | * prorotype 16 | */ 17 | KeyObject* create_key(void); 18 | void release_key(KeyObject* key); 19 | 20 | ValueObject* create_value(void); 21 | void release_value(ValueObject* value); 22 | 23 | /* 24 | * dict type 25 | */ 26 | unsigned int keyHashFunction(const void *key); 27 | int keyCompareFunction(void *privdata, const void *x, const void *y); 28 | void keyDestructor(void *privdata, void *key); 29 | void valDestructor(void *privdata, void *value); 30 | -------------------------------------------------------------------------------- /storage/reuse-redis-module/code/dict/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "int_dict_type.h" 5 | #include "dict.h" 6 | 7 | extern dictType intDictType; 8 | 9 | void test_empty_dict(void) 10 | { 11 | dict* d = dictCreate(&intDictType, NULL); 12 | 13 | dictRelease(d); 14 | } 15 | 16 | void test_add_and_delete_key_value_pair(void) 17 | { 18 | // 创建新字典 19 | dict *d = dictCreate(&intDictType, NULL); 20 | 21 | // 创建键和值 22 | KeyObject *k = create_key(); 23 | k->value = 1; 24 | ValueObject *v = create_value(); 25 | v->value = 10086; 26 | 27 | // 添加键值对 28 | dictAdd(d, k, v); 29 | 30 | assert( 31 | dictSize(d) == 1 32 | ); 33 | 34 | assert( 35 | dictFind(d, k) != NULL 36 | ); 37 | 38 | // 删除键值对 39 | dictDelete(d, k); 40 | 41 | assert( 42 | dictSize(d) == 0 43 | ); 44 | 45 | assert( 46 | dictFind(d, k) == NULL 47 | ); 48 | 49 | // 释放字典 50 | dictRelease(d); 51 | } 52 | 53 | void main(void) 54 | { 55 | 56 | test_empty_dict(); 57 | 58 | test_add_and_delete_key_value_pair(); 59 | 60 | } 61 | -------------------------------------------------------------------------------- /storage/reuse-redis-module/code/dict/zmalloc.h: -------------------------------------------------------------------------------- 1 | /* zmalloc - total amount of allocated memory aware version of malloc() 2 | * 3 | * Copyright (c) 2009-2010, Salvatore Sanfilippo 4 | * All rights reserved. 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions are met: 8 | * 9 | * * Redistributions of source code must retain the above copyright notice, 10 | * this list of conditions and the following disclaimer. 11 | * * Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * * Neither the name of Redis nor the names of its contributors may be used 15 | * to endorse or promote products derived from this software without 16 | * specific prior written permission. 17 | * 18 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 22 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 23 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 24 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 25 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 26 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 27 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 | * POSSIBILITY OF SUCH DAMAGE. 29 | */ 30 | 31 | #ifndef __ZMALLOC_H 32 | #define __ZMALLOC_H 33 | 34 | /* Double expansion needed for stringification of macro values. */ 35 | #define __xstr(s) __str(s) 36 | #define __str(s) #s 37 | 38 | #if defined(USE_TCMALLOC) 39 | #define ZMALLOC_LIB ("tcmalloc-" __xstr(TC_VERSION_MAJOR) "." __xstr(TC_VERSION_MINOR)) 40 | #include 41 | #if (TC_VERSION_MAJOR == 1 && TC_VERSION_MINOR >= 6) || (TC_VERSION_MAJOR > 1) 42 | #define HAVE_MALLOC_SIZE 1 43 | #define zmalloc_size(p) tc_malloc_size(p) 44 | #else 45 | #error "Newer version of tcmalloc required" 46 | #endif 47 | 48 | #elif defined(USE_JEMALLOC) 49 | #define ZMALLOC_LIB ("jemalloc-" __xstr(JEMALLOC_VERSION_MAJOR) "." __xstr(JEMALLOC_VERSION_MINOR) "." __xstr(JEMALLOC_VERSION_BUGFIX)) 50 | #include 51 | #if (JEMALLOC_VERSION_MAJOR == 2 && JEMALLOC_VERSION_MINOR >= 1) || (JEMALLOC_VERSION_MAJOR > 2) 52 | #define HAVE_MALLOC_SIZE 1 53 | #define zmalloc_size(p) je_malloc_usable_size(p) 54 | #else 55 | #error "Newer version of jemalloc required" 56 | #endif 57 | 58 | #elif defined(__APPLE__) 59 | #include 60 | #define HAVE_MALLOC_SIZE 1 61 | #define zmalloc_size(p) malloc_size(p) 62 | #endif 63 | 64 | #ifndef ZMALLOC_LIB 65 | #define ZMALLOC_LIB "libc" 66 | #endif 67 | 68 | void *zmalloc(size_t size); 69 | void *zcalloc(size_t size); 70 | void *zrealloc(void *ptr, size_t size); 71 | void zfree(void *ptr); 72 | char *zstrdup(const char *s); 73 | size_t zmalloc_used_memory(void); 74 | void zmalloc_enable_thread_safeness(void); 75 | void zmalloc_set_oom_handler(void (*oom_handler)(size_t)); 76 | float zmalloc_get_fragmentation_ratio(void); 77 | size_t zmalloc_get_rss(void); 78 | size_t zmalloc_get_private_dirty(void); 79 | void zlibc_free(void *ptr); 80 | 81 | #ifndef HAVE_MALLOC_SIZE 82 | size_t zmalloc_size(void *ptr); 83 | #endif 84 | 85 | #endif /* __ZMALLOC_H */ 86 | -------------------------------------------------------------------------------- /storage/reuse-redis-module/code/intset/endianconv.c: -------------------------------------------------------------------------------- 1 | /* endinconv.c -- Endian conversions utilities. 2 | * 3 | * This functions are never called directly, but always using the macros 4 | * defined into endianconv.h, this way we define everything is a non-operation 5 | * if the arch is already little endian. 6 | * 7 | * Redis tries to encode everything as little endian (but a few things that need 8 | * to be backward compatible are still in big endian) because most of the 9 | * production environments are little endian, and we have a lot of conversions 10 | * in a few places because ziplists, intsets, zipmaps, need to be endian-neutral 11 | * even in memory, since they are serialied on RDB files directly with a single 12 | * write(2) without other additional steps. 13 | * 14 | * ---------------------------------------------------------------------------- 15 | * 16 | * Copyright (c) 2011-2012, Salvatore Sanfilippo 17 | * All rights reserved. 18 | * 19 | * Redistribution and use in source and binary forms, with or without 20 | * modification, are permitted provided that the following conditions are met: 21 | * 22 | * * Redistributions of source code must retain the above copyright notice, 23 | * this list of conditions and the following disclaimer. 24 | * * Redistributions in binary form must reproduce the above copyright 25 | * notice, this list of conditions and the following disclaimer in the 26 | * documentation and/or other materials provided with the distribution. 27 | * * Neither the name of Redis nor the names of its contributors may be used 28 | * to endorse or promote products derived from this software without 29 | * specific prior written permission. 30 | * 31 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 32 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 33 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 34 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 35 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 36 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 37 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 38 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 39 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 40 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 41 | * POSSIBILITY OF SUCH DAMAGE. 42 | */ 43 | 44 | 45 | #include 46 | 47 | /* Toggle the 16 bit unsigned integer pointed by *p from little endian to 48 | * big endian */ 49 | void memrev16(void *p) { 50 | unsigned char *x = p, t; 51 | 52 | t = x[0]; 53 | x[0] = x[1]; 54 | x[1] = t; 55 | } 56 | 57 | /* Toggle the 32 bit unsigned integer pointed by *p from little endian to 58 | * big endian */ 59 | void memrev32(void *p) { 60 | unsigned char *x = p, t; 61 | 62 | t = x[0]; 63 | x[0] = x[3]; 64 | x[3] = t; 65 | t = x[1]; 66 | x[1] = x[2]; 67 | x[2] = t; 68 | } 69 | 70 | /* Toggle the 64 bit unsigned integer pointed by *p from little endian to 71 | * big endian */ 72 | void memrev64(void *p) { 73 | unsigned char *x = p, t; 74 | 75 | t = x[0]; 76 | x[0] = x[7]; 77 | x[7] = t; 78 | t = x[1]; 79 | x[1] = x[6]; 80 | x[6] = t; 81 | t = x[2]; 82 | x[2] = x[5]; 83 | x[5] = t; 84 | t = x[3]; 85 | x[3] = x[4]; 86 | x[4] = t; 87 | } 88 | 89 | uint16_t intrev16(uint16_t v) { 90 | memrev16(&v); 91 | return v; 92 | } 93 | 94 | uint32_t intrev32(uint32_t v) { 95 | memrev32(&v); 96 | return v; 97 | } 98 | 99 | uint64_t intrev64(uint64_t v) { 100 | memrev64(&v); 101 | return v; 102 | } 103 | 104 | #ifdef TESTMAIN 105 | #include 106 | 107 | int main(void) { 108 | char buf[32]; 109 | 110 | sprintf(buf,"ciaoroma"); 111 | memrev16(buf); 112 | printf("%s\n", buf); 113 | 114 | sprintf(buf,"ciaoroma"); 115 | memrev32(buf); 116 | printf("%s\n", buf); 117 | 118 | sprintf(buf,"ciaoroma"); 119 | memrev64(buf); 120 | printf("%s\n", buf); 121 | 122 | return 0; 123 | } 124 | #endif 125 | -------------------------------------------------------------------------------- /storage/reuse-redis-module/code/intset/endianconv.h: -------------------------------------------------------------------------------- 1 | /* See endianconv.c top comments for more information 2 | * 3 | * ---------------------------------------------------------------------------- 4 | * 5 | * Copyright (c) 2011-2012, Salvatore Sanfilippo 6 | * All rights reserved. 7 | * 8 | * Redistribution and use in source and binary forms, with or without 9 | * modification, are permitted provided that the following conditions are met: 10 | * 11 | * * Redistributions of source code must retain the above copyright notice, 12 | * this list of conditions and the following disclaimer. 13 | * * Redistributions in binary form must reproduce the above copyright 14 | * notice, this list of conditions and the following disclaimer in the 15 | * documentation and/or other materials provided with the distribution. 16 | * * Neither the name of Redis nor the names of its contributors may be used 17 | * to endorse or promote products derived from this software without 18 | * specific prior written permission. 19 | * 20 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 24 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 25 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 26 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 27 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 28 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 29 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 30 | * POSSIBILITY OF SUCH DAMAGE. 31 | */ 32 | 33 | #ifndef __ENDIANCONV_H 34 | #define __ENDIANCONV_H 35 | 36 | #include 37 | 38 | void memrev16(void *p); 39 | void memrev32(void *p); 40 | void memrev64(void *p); 41 | uint16_t intrev16(uint16_t v); 42 | uint32_t intrev32(uint32_t v); 43 | uint64_t intrev64(uint64_t v); 44 | 45 | /* variants of the function doing the actual convertion only if the target 46 | * host is big endian */ 47 | #if (BYTE_ORDER == LITTLE_ENDIAN) 48 | #define memrev16ifbe(p) 49 | #define memrev32ifbe(p) 50 | #define memrev64ifbe(p) 51 | #define intrev16ifbe(v) (v) 52 | #define intrev32ifbe(v) (v) 53 | #define intrev64ifbe(v) (v) 54 | #else 55 | #define memrev16ifbe(p) memrev16(p) 56 | #define memrev32ifbe(p) memrev32(p) 57 | #define memrev64ifbe(p) memrev64(p) 58 | #define intrev16ifbe(v) intrev16(v) 59 | #define intrev32ifbe(v) intrev32(v) 60 | #define intrev64ifbe(v) intrev64(v) 61 | #endif 62 | 63 | #endif 64 | -------------------------------------------------------------------------------- /storage/reuse-redis-module/code/intset/intset.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2009-2012, Pieter Noordhuis 3 | * Copyright (c) 2009-2012, Salvatore Sanfilippo 4 | * All rights reserved. 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions are met: 8 | * 9 | * * Redistributions of source code must retain the above copyright notice, 10 | * this list of conditions and the following disclaimer. 11 | * * Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * * Neither the name of Redis nor the names of its contributors may be used 15 | * to endorse or promote products derived from this software without 16 | * specific prior written permission. 17 | * 18 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 22 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 23 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 24 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 25 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 26 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 27 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 | * POSSIBILITY OF SUCH DAMAGE. 29 | */ 30 | 31 | #ifndef __INTSET_H 32 | #define __INTSET_H 33 | #include 34 | #include 35 | 36 | typedef struct intset { 37 | 38 | // 保存元素所使用的类型的长度 39 | uint32_t encoding; 40 | 41 | // 元素个数 42 | uint32_t length; 43 | 44 | // 保存元素的数组 45 | int8_t contents[]; 46 | 47 | } intset; 48 | 49 | intset *intsetNew(void); 50 | intset *intsetAdd(intset *is, int64_t value, uint8_t *success); 51 | intset *intsetRemove(intset *is, int64_t value, int *success); 52 | uint8_t intsetFind(intset *is, int64_t value); 53 | int64_t intsetRandom(intset *is); 54 | uint8_t intsetGet(intset *is, uint32_t pos, int64_t *value); 55 | uint32_t intsetLen(intset *is); 56 | size_t intsetBlobLen(intset *is); 57 | 58 | #endif // __INTSET_H 59 | -------------------------------------------------------------------------------- /storage/reuse-redis-module/code/intset/main.c: -------------------------------------------------------------------------------- 1 | // main.c 2 | 3 | #include // 载入 NULL 4 | #include 5 | 6 | #include "intset.h" 7 | #include "endianconv.h" 8 | 9 | // Redis 将以下常量定义在了 intset.c 10 | // 所以我们需要重新定义一次 11 | // 如果将 intset.c 中的这些定义移动到 intset.h 12 | // 那么这里的定义就可以省去了 13 | #define INTSET_ENC_INT16 (sizeof(int16_t)) 14 | #define INTSET_ENC_INT32 (sizeof(int32_t)) 15 | #define INTSET_ENC_INT64 (sizeof(int64_t)) 16 | 17 | void test_create_and_destroy_intset(void) 18 | { 19 | intset *is = intsetNew(); 20 | 21 | assert( 22 | is != NULL 23 | ); 24 | 25 | assert( 26 | intsetLen(is) == 0 27 | ); 28 | 29 | zfree(is); 30 | } 31 | 32 | void test_add_remove_and_find_element_in_intset(void) 33 | { 34 | intset *is = intsetNew(); 35 | 36 | assert( 37 | is != NULL 38 | ); 39 | 40 | uint8_t success = 0; 41 | 42 | // add element 43 | is = intsetAdd(is, 1, &success); 44 | assert( 45 | success == 1 && 46 | intsetLen(is) == 1 47 | ); 48 | 49 | // add another element 50 | is = intsetAdd(is, 2, &success); 51 | assert( 52 | success == 1 && 53 | intsetLen(is) == 2 54 | ); 55 | 56 | // existsed element won't be add again 57 | is = intsetAdd(is, 2, &success); 58 | assert( 59 | success == 0 && 60 | intsetLen(is) == 2 61 | ); 62 | 63 | // remove 2 from is 64 | int int_success = 0; 65 | is = intsetRemove(is, 2, &int_success); 66 | assert( 67 | int_success == 1 && 68 | intsetLen(is) == 1 && 69 | intsetFind(is, 2) == 0 70 | ); 71 | 72 | zfree(is); 73 | } 74 | 75 | void test_intset_upgrade(void) 76 | { 77 | intset *is = intsetNew(); 78 | 79 | is = intsetAdd(is, 32, NULL); 80 | assert( 81 | intrev32ifbe(is->encoding) == INTSET_ENC_INT16 82 | ); 83 | 84 | is = intsetAdd(is, 65535, NULL); 85 | assert( 86 | intrev32ifbe(is->encoding) == INTSET_ENC_INT32 87 | ); 88 | 89 | is = intsetAdd(is, 4294967295u, NULL); 90 | assert( 91 | intrev32ifbe(is->encoding) == INTSET_ENC_INT64 92 | ); 93 | 94 | zfree(is); 95 | } 96 | 97 | void main(void) 98 | { 99 | 100 | test_create_and_destroy_intset(); 101 | 102 | test_add_remove_and_find_element_in_intset(); 103 | 104 | test_intset_upgrade(); 105 | 106 | } 107 | -------------------------------------------------------------------------------- /storage/reuse-redis-module/code/intset/zmalloc.h: -------------------------------------------------------------------------------- 1 | /* zmalloc - total amount of allocated memory aware version of malloc() 2 | * 3 | * Copyright (c) 2009-2010, Salvatore Sanfilippo 4 | * All rights reserved. 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions are met: 8 | * 9 | * * Redistributions of source code must retain the above copyright notice, 10 | * this list of conditions and the following disclaimer. 11 | * * Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * * Neither the name of Redis nor the names of its contributors may be used 15 | * to endorse or promote products derived from this software without 16 | * specific prior written permission. 17 | * 18 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 22 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 23 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 24 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 25 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 26 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 27 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 | * POSSIBILITY OF SUCH DAMAGE. 29 | */ 30 | 31 | #ifndef __ZMALLOC_H 32 | #define __ZMALLOC_H 33 | 34 | /* Double expansion needed for stringification of macro values. */ 35 | #define __xstr(s) __str(s) 36 | #define __str(s) #s 37 | 38 | #if defined(USE_TCMALLOC) 39 | #define ZMALLOC_LIB ("tcmalloc-" __xstr(TC_VERSION_MAJOR) "." __xstr(TC_VERSION_MINOR)) 40 | #include 41 | #if (TC_VERSION_MAJOR == 1 && TC_VERSION_MINOR >= 6) || (TC_VERSION_MAJOR > 1) 42 | #define HAVE_MALLOC_SIZE 1 43 | #define zmalloc_size(p) tc_malloc_size(p) 44 | #else 45 | #error "Newer version of tcmalloc required" 46 | #endif 47 | 48 | #elif defined(USE_JEMALLOC) 49 | #define ZMALLOC_LIB ("jemalloc-" __xstr(JEMALLOC_VERSION_MAJOR) "." __xstr(JEMALLOC_VERSION_MINOR) "." __xstr(JEMALLOC_VERSION_BUGFIX)) 50 | #include 51 | #if (JEMALLOC_VERSION_MAJOR == 2 && JEMALLOC_VERSION_MINOR >= 1) || (JEMALLOC_VERSION_MAJOR > 2) 52 | #define HAVE_MALLOC_SIZE 1 53 | #define zmalloc_size(p) je_malloc_usable_size(p) 54 | #else 55 | #error "Newer version of jemalloc required" 56 | #endif 57 | 58 | #elif defined(__APPLE__) 59 | #include 60 | #define HAVE_MALLOC_SIZE 1 61 | #define zmalloc_size(p) malloc_size(p) 62 | #endif 63 | 64 | #ifndef ZMALLOC_LIB 65 | #define ZMALLOC_LIB "libc" 66 | #endif 67 | 68 | void *zmalloc(size_t size); 69 | void *zcalloc(size_t size); 70 | void *zrealloc(void *ptr, size_t size); 71 | void zfree(void *ptr); 72 | char *zstrdup(const char *s); 73 | size_t zmalloc_used_memory(void); 74 | void zmalloc_enable_thread_safeness(void); 75 | void zmalloc_set_oom_handler(void (*oom_handler)(size_t)); 76 | float zmalloc_get_fragmentation_ratio(void); 77 | size_t zmalloc_get_rss(void); 78 | size_t zmalloc_get_private_dirty(void); 79 | void zlibc_free(void *ptr); 80 | 81 | #ifndef HAVE_MALLOC_SIZE 82 | size_t zmalloc_size(void *ptr); 83 | #endif 84 | 85 | #endif /* __ZMALLOC_H */ 86 | -------------------------------------------------------------------------------- /storage/reuse-redis-module/code/sds/main.c: -------------------------------------------------------------------------------- 1 | // main.c 2 | 3 | #include 4 | #include 5 | #include "sds.h" 6 | 7 | void create_sds_and_check_its_property(void) 8 | { 9 | sds s = sdsnew("hello"); 10 | 11 | // 验证长度 12 | assert( 13 | sdslen(s) == 5 14 | ); 15 | 16 | // 验证空白位置 17 | assert( 18 | sdsavail(s) == 0 19 | ); 20 | 21 | // 验证已有内容 22 | assert( 23 | memcmp(s, "hello\0", 6) == 0 24 | ); 25 | 26 | // 释放 27 | sdsfree(s); 28 | } 29 | 30 | 31 | void test_sdsdup(void) 32 | { 33 | sds s = sdsnew("hello"); 34 | 35 | // 创建 s 的一个副本 36 | sds another = sdsdup(s); 37 | 38 | // 长度对比 39 | assert( 40 | sdslen(s) == sdslen(another) 41 | ); 42 | 43 | // 空间对比 44 | assert( 45 | sdsavail(s) == sdsavail(another) 46 | ); 47 | 48 | // 内容对比 49 | assert( 50 | memcmp(s, another, 6) == 0 51 | ); 52 | 53 | // 释放 54 | sdsfree(s); 55 | sdsfree(another); 56 | } 57 | 58 | 59 | void test_sdscat(void) 60 | { 61 | const char const total[] = "hello moto\0"; 62 | 63 | sds s = sdsnew("hello"); 64 | 65 | // 追加内容 66 | const char const append[] = " moto"; 67 | sdscat(s, append); 68 | 69 | // 长度对比 70 | assert( 71 | sdslen(s) == strlen(total) 72 | ); 73 | 74 | // 空间对比 75 | assert( 76 | // 追加之后的字符串 77 | // 会预留大小相当于现有字符串长度的空间 78 | sdsavail(s) == sdslen(s) 79 | ); 80 | 81 | // 内容对比 82 | assert( 83 | memcmp(s, total, strlen(total)+1) == 0 84 | ); 85 | 86 | // 释放空间 87 | sdsfree(s); 88 | } 89 | 90 | 91 | int main(void) 92 | { 93 | create_sds_and_check_its_property(); 94 | 95 | test_sdsdup(); 96 | 97 | test_sdscat(); 98 | } 99 | -------------------------------------------------------------------------------- /storage/reuse-redis-module/code/sds/sds.h: -------------------------------------------------------------------------------- 1 | /* SDSLib, A C dynamic strings library 2 | * 3 | * Copyright (c) 2006-2010, Salvatore Sanfilippo 4 | * All rights reserved. 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions are met: 8 | * 9 | * * Redistributions of source code must retain the above copyright notice, 10 | * this list of conditions and the following disclaimer. 11 | * * Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * * Neither the name of Redis nor the names of its contributors may be used 15 | * to endorse or promote products derived from this software without 16 | * specific prior written permission. 17 | * 18 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 22 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 23 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 24 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 25 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 26 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 27 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 | * POSSIBILITY OF SUCH DAMAGE. 29 | */ 30 | 31 | #ifndef __SDS_H 32 | #define __SDS_H 33 | 34 | #define SDS_MAX_PREALLOC (1024*1024) 35 | 36 | #include 37 | #include 38 | 39 | // sds 类型 40 | typedef char *sds; 41 | 42 | // sdshdr 结构 43 | struct sdshdr { 44 | 45 | // buf 已占用长度 46 | int len; 47 | 48 | // buf 剩余可用长度 49 | int free; 50 | 51 | // 实际保存字符串数据的地方 52 | char buf[]; 53 | }; 54 | 55 | /* 56 | * 返回 sds buf 的已占用长度 57 | */ 58 | static inline size_t sdslen(const sds s) { 59 | struct sdshdr *sh = (void*)(s-(sizeof(struct sdshdr))); 60 | return sh->len; 61 | } 62 | 63 | /* 64 | * 返回 sds buf 的可用长度 65 | */ 66 | static inline size_t sdsavail(const sds s) { 67 | struct sdshdr *sh = (void*)(s-(sizeof(struct sdshdr))); 68 | return sh->free; 69 | } 70 | 71 | sds sdsnewlen(const void *init, size_t initlen); 72 | sds sdsnew(const char *init); 73 | sds sdsempty(); 74 | size_t sdslen(const sds s); 75 | sds sdsdup(const sds s); 76 | void sdsfree(sds s); 77 | size_t sdsavail(const sds s); 78 | sds sdsgrowzero(sds s, size_t len); 79 | sds sdscatlen(sds s, const void *t, size_t len); 80 | sds sdscat(sds s, const char *t); 81 | sds sdscatsds(sds s, const sds t); 82 | sds sdscpylen(sds s, const char *t, size_t len); 83 | sds sdscpy(sds s, const char *t); 84 | 85 | sds sdscatvprintf(sds s, const char *fmt, va_list ap); 86 | #ifdef __GNUC__ 87 | sds sdscatprintf(sds s, const char *fmt, ...) 88 | __attribute__((format(printf, 2, 3))); 89 | #else 90 | sds sdscatprintf(sds s, const char *fmt, ...); 91 | #endif 92 | 93 | sds sdstrim(sds s, const char *cset); 94 | sds sdsrange(sds s, int start, int end); 95 | void sdsupdatelen(sds s); 96 | void sdsclear(sds s); 97 | int sdscmp(const sds s1, const sds s2); 98 | sds *sdssplitlen(const char *s, int len, const char *sep, int seplen, int *count); 99 | void sdsfreesplitres(sds *tokens, int count); 100 | void sdstolower(sds s); 101 | void sdstoupper(sds s); 102 | sds sdsfromlonglong(long long value); 103 | sds sdscatrepr(sds s, const char *p, size_t len); 104 | sds *sdssplitargs(const char *line, int *argc); 105 | void sdssplitargs_free(sds *argv, int argc); 106 | sds sdsmapchars(sds s, const char *from, const char *to, size_t setlen); 107 | 108 | /* Low level functions exposed to the user API */ 109 | sds sdsMakeRoomFor(sds s, size_t addlen); 110 | void sdsIncrLen(sds s, int incr); 111 | sds sdsRemoveFreeSpace(sds s); 112 | size_t sdsAllocSize(sds s); 113 | 114 | #endif 115 | -------------------------------------------------------------------------------- /storage/reuse-redis-module/code/sds/zmalloc.h: -------------------------------------------------------------------------------- 1 | /* zmalloc - total amount of allocated memory aware version of malloc() 2 | * 3 | * Copyright (c) 2009-2010, Salvatore Sanfilippo 4 | * All rights reserved. 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions are met: 8 | * 9 | * * Redistributions of source code must retain the above copyright notice, 10 | * this list of conditions and the following disclaimer. 11 | * * Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * * Neither the name of Redis nor the names of its contributors may be used 15 | * to endorse or promote products derived from this software without 16 | * specific prior written permission. 17 | * 18 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 22 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 23 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 24 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 25 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 26 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 27 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 | * POSSIBILITY OF SUCH DAMAGE. 29 | */ 30 | 31 | #ifndef __ZMALLOC_H 32 | #define __ZMALLOC_H 33 | 34 | /* Double expansion needed for stringification of macro values. */ 35 | #define __xstr(s) __str(s) 36 | #define __str(s) #s 37 | 38 | #if defined(USE_TCMALLOC) 39 | #define ZMALLOC_LIB ("tcmalloc-" __xstr(TC_VERSION_MAJOR) "." __xstr(TC_VERSION_MINOR)) 40 | #include 41 | #if (TC_VERSION_MAJOR == 1 && TC_VERSION_MINOR >= 6) || (TC_VERSION_MAJOR > 1) 42 | #define HAVE_MALLOC_SIZE 1 43 | #define zmalloc_size(p) tc_malloc_size(p) 44 | #else 45 | #error "Newer version of tcmalloc required" 46 | #endif 47 | 48 | #elif defined(USE_JEMALLOC) 49 | #define ZMALLOC_LIB ("jemalloc-" __xstr(JEMALLOC_VERSION_MAJOR) "." __xstr(JEMALLOC_VERSION_MINOR) "." __xstr(JEMALLOC_VERSION_BUGFIX)) 50 | #include 51 | #if (JEMALLOC_VERSION_MAJOR == 2 && JEMALLOC_VERSION_MINOR >= 1) || (JEMALLOC_VERSION_MAJOR > 2) 52 | #define HAVE_MALLOC_SIZE 1 53 | #define zmalloc_size(p) je_malloc_usable_size(p) 54 | #else 55 | #error "Newer version of jemalloc required" 56 | #endif 57 | 58 | #elif defined(__APPLE__) 59 | #include 60 | #define HAVE_MALLOC_SIZE 1 61 | #define zmalloc_size(p) malloc_size(p) 62 | #endif 63 | 64 | #ifndef ZMALLOC_LIB 65 | #define ZMALLOC_LIB "libc" 66 | #endif 67 | 68 | #include 69 | 70 | void *zmalloc(size_t size); 71 | void *zcalloc(size_t size); 72 | void *zrealloc(void *ptr, size_t size); 73 | void zfree(void *ptr); 74 | char *zstrdup(const char *s); 75 | size_t zmalloc_used_memory(void); 76 | void zmalloc_enable_thread_safeness(void); 77 | void zmalloc_set_oom_handler(void (*oom_handler)(size_t)); 78 | float zmalloc_get_fragmentation_ratio(void); 79 | size_t zmalloc_get_rss(void); 80 | size_t zmalloc_get_private_dirty(void); 81 | void zlibc_free(void *ptr); 82 | 83 | #ifndef HAVE_MALLOC_SIZE 84 | size_t zmalloc_size(void *ptr); 85 | #endif 86 | 87 | #endif /* __ZMALLOC_H */ 88 | -------------------------------------------------------------------------------- /storage/reuse-redis-module/code/ziplist/endianconv.c: -------------------------------------------------------------------------------- 1 | /* endinconv.c -- Endian conversions utilities. 2 | * 3 | * This functions are never called directly, but always using the macros 4 | * defined into endianconv.h, this way we define everything is a non-operation 5 | * if the arch is already little endian. 6 | * 7 | * Redis tries to encode everything as little endian (but a few things that need 8 | * to be backward compatible are still in big endian) because most of the 9 | * production environments are little endian, and we have a lot of conversions 10 | * in a few places because ziplists, intsets, zipmaps, need to be endian-neutral 11 | * even in memory, since they are serialied on RDB files directly with a single 12 | * write(2) without other additional steps. 13 | * 14 | * ---------------------------------------------------------------------------- 15 | * 16 | * Copyright (c) 2011-2012, Salvatore Sanfilippo 17 | * All rights reserved. 18 | * 19 | * Redistribution and use in source and binary forms, with or without 20 | * modification, are permitted provided that the following conditions are met: 21 | * 22 | * * Redistributions of source code must retain the above copyright notice, 23 | * this list of conditions and the following disclaimer. 24 | * * Redistributions in binary form must reproduce the above copyright 25 | * notice, this list of conditions and the following disclaimer in the 26 | * documentation and/or other materials provided with the distribution. 27 | * * Neither the name of Redis nor the names of its contributors may be used 28 | * to endorse or promote products derived from this software without 29 | * specific prior written permission. 30 | * 31 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 32 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 33 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 34 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 35 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 36 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 37 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 38 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 39 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 40 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 41 | * POSSIBILITY OF SUCH DAMAGE. 42 | */ 43 | 44 | 45 | #include 46 | 47 | /* Toggle the 16 bit unsigned integer pointed by *p from little endian to 48 | * big endian */ 49 | void memrev16(void *p) { 50 | unsigned char *x = p, t; 51 | 52 | t = x[0]; 53 | x[0] = x[1]; 54 | x[1] = t; 55 | } 56 | 57 | /* Toggle the 32 bit unsigned integer pointed by *p from little endian to 58 | * big endian */ 59 | void memrev32(void *p) { 60 | unsigned char *x = p, t; 61 | 62 | t = x[0]; 63 | x[0] = x[3]; 64 | x[3] = t; 65 | t = x[1]; 66 | x[1] = x[2]; 67 | x[2] = t; 68 | } 69 | 70 | /* Toggle the 64 bit unsigned integer pointed by *p from little endian to 71 | * big endian */ 72 | void memrev64(void *p) { 73 | unsigned char *x = p, t; 74 | 75 | t = x[0]; 76 | x[0] = x[7]; 77 | x[7] = t; 78 | t = x[1]; 79 | x[1] = x[6]; 80 | x[6] = t; 81 | t = x[2]; 82 | x[2] = x[5]; 83 | x[5] = t; 84 | t = x[3]; 85 | x[3] = x[4]; 86 | x[4] = t; 87 | } 88 | 89 | uint16_t intrev16(uint16_t v) { 90 | memrev16(&v); 91 | return v; 92 | } 93 | 94 | uint32_t intrev32(uint32_t v) { 95 | memrev32(&v); 96 | return v; 97 | } 98 | 99 | uint64_t intrev64(uint64_t v) { 100 | memrev64(&v); 101 | return v; 102 | } 103 | 104 | #ifdef TESTMAIN 105 | #include 106 | 107 | int main(void) { 108 | char buf[32]; 109 | 110 | sprintf(buf,"ciaoroma"); 111 | memrev16(buf); 112 | printf("%s\n", buf); 113 | 114 | sprintf(buf,"ciaoroma"); 115 | memrev32(buf); 116 | printf("%s\n", buf); 117 | 118 | sprintf(buf,"ciaoroma"); 119 | memrev64(buf); 120 | printf("%s\n", buf); 121 | 122 | return 0; 123 | } 124 | #endif 125 | -------------------------------------------------------------------------------- /storage/reuse-redis-module/code/ziplist/endianconv.h: -------------------------------------------------------------------------------- 1 | /* See endianconv.c top comments for more information 2 | * 3 | * ---------------------------------------------------------------------------- 4 | * 5 | * Copyright (c) 2011-2012, Salvatore Sanfilippo 6 | * All rights reserved. 7 | * 8 | * Redistribution and use in source and binary forms, with or without 9 | * modification, are permitted provided that the following conditions are met: 10 | * 11 | * * Redistributions of source code must retain the above copyright notice, 12 | * this list of conditions and the following disclaimer. 13 | * * Redistributions in binary form must reproduce the above copyright 14 | * notice, this list of conditions and the following disclaimer in the 15 | * documentation and/or other materials provided with the distribution. 16 | * * Neither the name of Redis nor the names of its contributors may be used 17 | * to endorse or promote products derived from this software without 18 | * specific prior written permission. 19 | * 20 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 24 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 25 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 26 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 27 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 28 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 29 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 30 | * POSSIBILITY OF SUCH DAMAGE. 31 | */ 32 | 33 | #ifndef __ENDIANCONV_H 34 | #define __ENDIANCONV_H 35 | 36 | #include 37 | 38 | void memrev16(void *p); 39 | void memrev32(void *p); 40 | void memrev64(void *p); 41 | uint16_t intrev16(uint16_t v); 42 | uint32_t intrev32(uint32_t v); 43 | uint64_t intrev64(uint64_t v); 44 | 45 | /* variants of the function doing the actual convertion only if the target 46 | * host is big endian */ 47 | #if (BYTE_ORDER == LITTLE_ENDIAN) 48 | #define memrev16ifbe(p) 49 | #define memrev32ifbe(p) 50 | #define memrev64ifbe(p) 51 | #define intrev16ifbe(v) (v) 52 | #define intrev32ifbe(v) (v) 53 | #define intrev64ifbe(v) (v) 54 | #else 55 | #define memrev16ifbe(p) memrev16(p) 56 | #define memrev32ifbe(p) memrev32(p) 57 | #define memrev64ifbe(p) memrev64(p) 58 | #define intrev16ifbe(v) intrev16(v) 59 | #define intrev32ifbe(v) intrev32(v) 60 | #define intrev64ifbe(v) intrev64(v) 61 | #endif 62 | 63 | #endif 64 | -------------------------------------------------------------------------------- /storage/reuse-redis-module/code/ziplist/fmacros.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2009-2012, Salvatore Sanfilippo 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * * Redistributions of source code must retain the above copyright notice, 9 | * this list of conditions and the following disclaimer. 10 | * * Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * * Neither the name of Redis nor the names of its contributors may be used 14 | * to endorse or promote products derived from this software without 15 | * specific prior written permission. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 21 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27 | * POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | 30 | #ifndef _REDIS_FMACRO_H 31 | #define _REDIS_FMACRO_H 32 | 33 | #define _BSD_SOURCE 34 | 35 | #if defined(__linux__) 36 | #define _GNU_SOURCE 37 | #endif 38 | 39 | #if defined(__linux__) || defined(__OpenBSD__) 40 | #define _XOPEN_SOURCE 700 41 | #else 42 | #define _XOPEN_SOURCE 43 | #endif 44 | 45 | #define _LARGEFILE_SOURCE 46 | #define _FILE_OFFSET_BITS 64 47 | 48 | #endif 49 | -------------------------------------------------------------------------------- /storage/reuse-redis-module/code/ziplist/main.c: -------------------------------------------------------------------------------- 1 | // main.c 2 | 3 | #include // load assert() 4 | #include // load NULL 5 | 6 | #include "ziplist.h" 7 | 8 | void test_create_and_destroy_ziplist(void) 9 | { 10 | unsigned char *zl = ziplistNew(); 11 | 12 | assert( 13 | zl != NULL 14 | ); 15 | 16 | zfree(zl); 17 | } 18 | 19 | void test_add_delete_and_find_node_wtih_ziplist(void) 20 | { 21 | unsigned char *zl = ziplistNew(); 22 | 23 | // add first node 24 | zl = ziplistPush(zl, (unsigned char*)"hello", 5, ZIPLIST_TAIL); 25 | 26 | assert( 27 | ziplistLen(zl) == 1 28 | ); 29 | 30 | assert( 31 | ziplistFind(zl, (unsigned char*)"hello", 5, 0) != NULL) 32 | ; 33 | 34 | // add second node 35 | zl = ziplistPush(zl, (unsigned char*)"moto", 4, ZIPLIST_TAIL); 36 | 37 | assert( 38 | ziplistLen(zl) == 2 39 | ); 40 | 41 | assert( 42 | ziplistFind(zl, (unsigned char*)"moto", 4, 0) != NULL); 43 | 44 | // delete first node 45 | unsigned char* node_p = ziplistFind(zl, (unsigned char*)"hello", 5, 0); 46 | zl = ziplistDelete(zl, &node_p); 47 | 48 | assert( 49 | ziplistFind(zl, (unsigned char*)"hello", 5, 0) == NULL 50 | ); 51 | 52 | assert( 53 | ziplistLen(zl) == 1 54 | ); 55 | 56 | // release memory 57 | zfree(zl); 58 | } 59 | 60 | void main(void) 61 | { 62 | test_create_and_destroy_ziplist(); 63 | 64 | test_add_delete_and_find_node_wtih_ziplist(); 65 | } 66 | -------------------------------------------------------------------------------- /storage/reuse-redis-module/code/ziplist/util.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2009-2012, Salvatore Sanfilippo 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * * Redistributions of source code must retain the above copyright notice, 9 | * this list of conditions and the following disclaimer. 10 | * * Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * * Neither the name of Redis nor the names of its contributors may be used 14 | * to endorse or promote products derived from this software without 15 | * specific prior written permission. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 21 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27 | * POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | 30 | #ifndef __REDIS_UTIL_H 31 | #define __REDIS_UTIL_H 32 | 33 | int stringmatchlen(const char *p, int plen, const char *s, int slen, int nocase); 34 | int stringmatch(const char *p, const char *s, int nocase); 35 | long long memtoll(const char *p, int *err); 36 | int ll2string(char *s, size_t len, long long value); 37 | int string2ll(const char *s, size_t slen, long long *value); 38 | int string2l(const char *s, size_t slen, long *value); 39 | int d2string(char *buf, size_t len, double value); 40 | 41 | #endif 42 | -------------------------------------------------------------------------------- /storage/reuse-redis-module/code/ziplist/ziplist.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2009-2012, Pieter Noordhuis 3 | * Copyright (c) 2009-2012, Salvatore Sanfilippo 4 | * All rights reserved. 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions are met: 8 | * 9 | * * Redistributions of source code must retain the above copyright notice, 10 | * this list of conditions and the following disclaimer. 11 | * * Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * * Neither the name of Redis nor the names of its contributors may be used 15 | * to endorse or promote products derived from this software without 16 | * specific prior written permission. 17 | * 18 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 22 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 23 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 24 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 25 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 26 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 27 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 | * POSSIBILITY OF SUCH DAMAGE. 29 | */ 30 | 31 | #define ZIPLIST_HEAD 0 32 | #define ZIPLIST_TAIL 1 33 | 34 | unsigned char *ziplistNew(void); 35 | unsigned char *ziplistPush(unsigned char *zl, unsigned char *s, unsigned int slen, int where); 36 | unsigned char *ziplistIndex(unsigned char *zl, int index); 37 | unsigned char *ziplistNext(unsigned char *zl, unsigned char *p); 38 | unsigned char *ziplistPrev(unsigned char *zl, unsigned char *p); 39 | unsigned int ziplistGet(unsigned char *p, unsigned char **sval, unsigned int *slen, long long *lval); 40 | unsigned char *ziplistInsert(unsigned char *zl, unsigned char *p, unsigned char *s, unsigned int slen); 41 | unsigned char *ziplistDelete(unsigned char *zl, unsigned char **p); 42 | unsigned char *ziplistDeleteRange(unsigned char *zl, unsigned int index, unsigned int num); 43 | unsigned int ziplistCompare(unsigned char *p, unsigned char *s, unsigned int slen); 44 | unsigned char *ziplistFind(unsigned char *p, unsigned char *vstr, unsigned int vlen, unsigned int skip); 45 | unsigned int ziplistLen(unsigned char *zl); 46 | size_t ziplistBlobLen(unsigned char *zl); 47 | -------------------------------------------------------------------------------- /storage/reuse-redis-module/code/ziplist/zmalloc.h: -------------------------------------------------------------------------------- 1 | /* zmalloc - total amount of allocated memory aware version of malloc() 2 | * 3 | * Copyright (c) 2009-2010, Salvatore Sanfilippo 4 | * All rights reserved. 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions are met: 8 | * 9 | * * Redistributions of source code must retain the above copyright notice, 10 | * this list of conditions and the following disclaimer. 11 | * * Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * * Neither the name of Redis nor the names of its contributors may be used 15 | * to endorse or promote products derived from this software without 16 | * specific prior written permission. 17 | * 18 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 22 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 23 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 24 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 25 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 26 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 27 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 | * POSSIBILITY OF SUCH DAMAGE. 29 | */ 30 | 31 | #ifndef __ZMALLOC_H 32 | #define __ZMALLOC_H 33 | 34 | /* Double expansion needed for stringification of macro values. */ 35 | #define __xstr(s) __str(s) 36 | #define __str(s) #s 37 | 38 | #if defined(USE_TCMALLOC) 39 | #define ZMALLOC_LIB ("tcmalloc-" __xstr(TC_VERSION_MAJOR) "." __xstr(TC_VERSION_MINOR)) 40 | #include 41 | #if (TC_VERSION_MAJOR == 1 && TC_VERSION_MINOR >= 6) || (TC_VERSION_MAJOR > 1) 42 | #define HAVE_MALLOC_SIZE 1 43 | #define zmalloc_size(p) tc_malloc_size(p) 44 | #else 45 | #error "Newer version of tcmalloc required" 46 | #endif 47 | 48 | #elif defined(USE_JEMALLOC) 49 | #define ZMALLOC_LIB ("jemalloc-" __xstr(JEMALLOC_VERSION_MAJOR) "." __xstr(JEMALLOC_VERSION_MINOR) "." __xstr(JEMALLOC_VERSION_BUGFIX)) 50 | #include 51 | #if (JEMALLOC_VERSION_MAJOR == 2 && JEMALLOC_VERSION_MINOR >= 1) || (JEMALLOC_VERSION_MAJOR > 2) 52 | #define HAVE_MALLOC_SIZE 1 53 | #define zmalloc_size(p) je_malloc_usable_size(p) 54 | #else 55 | #error "Newer version of jemalloc required" 56 | #endif 57 | 58 | #elif defined(__APPLE__) 59 | #include 60 | #define HAVE_MALLOC_SIZE 1 61 | #define zmalloc_size(p) malloc_size(p) 62 | #endif 63 | 64 | #ifndef ZMALLOC_LIB 65 | #define ZMALLOC_LIB "libc" 66 | #endif 67 | 68 | void *zmalloc(size_t size); 69 | void *zcalloc(size_t size); 70 | void *zrealloc(void *ptr, size_t size); 71 | void zfree(void *ptr); 72 | char *zstrdup(const char *s); 73 | size_t zmalloc_used_memory(void); 74 | void zmalloc_enable_thread_safeness(void); 75 | void zmalloc_set_oom_handler(void (*oom_handler)(size_t)); 76 | float zmalloc_get_fragmentation_ratio(void); 77 | size_t zmalloc_get_rss(void); 78 | size_t zmalloc_get_private_dirty(void); 79 | void zlibc_free(void *ptr); 80 | 81 | #ifndef HAVE_MALLOC_SIZE 82 | size_t zmalloc_size(void *ptr); 83 | #endif 84 | 85 | #endif /* __ZMALLOC_H */ 86 | -------------------------------------------------------------------------------- /storage/reuse-redis-module/dict.rst: -------------------------------------------------------------------------------- 1 | .. highlight:: c 2 | 3 | 字典 4 | ========= 5 | 6 | 7 | 适用范围 8 | -------------- 9 | 10 | Redis 的字典实现具有以下特色: 11 | 12 | 1. 可以通过设置不同的类型特定函数,让字典的键和值包含不同的类型,并为键使用不同的哈希算法。 13 | 14 | 2. 字典可以自动进行扩展或收缩,并且由此而带来的 rehash 是渐进式地进行的。 15 | 16 | 字典的相关设计和操作 API 可以参考 `《Redis 设计与实现》 `_ 的相关章节。 17 | 18 | 19 | 准备工作 20 | -------------- 21 | 22 | 1. 将 ``dict.h`` 、 ``dict.c`` 、 ``fmacros.h`` 、 ``zmalloc.h`` 和 ``zmalloc.c`` 文件复制到目标文件夹。 23 | 24 | 2. 将 ``zmalloc.c`` 中的 ``#include "config.h"`` 注释掉。 25 | 26 | 27 | 类型特定函数 28 | -------------- 29 | 30 | Redis 字典允许使用者为字典的键和值设置不同的类型特定函数, 31 | 从而使字典可以包含各种不同类型的键和值, 32 | 这些函数可以通过 ``dictType`` 类型定义: 33 | 34 | :: 35 | 36 | // 摘录自 dict.h 37 | 38 | /* 39 | * 特定于类型的一簇处理函数 40 | */ 41 | typedef struct dictType { 42 | 43 | // 计算键的哈希值函数 44 | unsigned int (*hashFunction)(const void *key); 45 | 46 | // 复制键的函数 47 | void *(*keyDup)(void *privdata, const void *key); 48 | 49 | // 复制值的函数 50 | void *(*valDup)(void *privdata, const void *obj); 51 | 52 | // 对比两个键的函数 53 | int (*keyCompare)(void *privdata, const void *key1, const void *key2); 54 | 55 | // 键的释构函数 56 | void (*keyDestructor)(void *privdata, void *key); 57 | 58 | // 值的释构函数 59 | void (*valDestructor)(void *privdata, void *obj); 60 | 61 | } dictType; 62 | 63 | 以下 ``dictType`` 定义了一个非常简单的, 64 | 键和值都是整数的字典类型: 65 | 66 | .. literalinclude:: code/dict/int_dict_type.h 67 | 68 | .. literalinclude:: code/dict/int_dict_type.c 69 | 70 | 71 | 测试驱动程序 72 | --------------------- 73 | 74 | 以下测试驱动程序使用了上面提到的整数字典类型, 75 | 并对字典的创建、键值对添加和删除等操作进行了测试。 76 | 77 | .. literalinclude:: code/dict/main.c 78 | 79 | 80 | 完整源码 81 | -------------------- 82 | 83 | 测试程序的完整源码可以在 `这里 `_ 的 ``dict`` 文件夹下找到。 84 | -------------------------------------------------------------------------------- /storage/reuse-redis-module/index.rst: -------------------------------------------------------------------------------- 1 | 重用 Redis 模块 2 | ====================== 3 | 4 | .. toctree:: 5 | :maxdepth: 2 6 | 7 | sds 8 | adlist 9 | dict 10 | intset 11 | ziplist 12 | -------------------------------------------------------------------------------- /storage/reuse-redis-module/intset.rst: -------------------------------------------------------------------------------- 1 | .. highlight:: c 2 | 3 | 整数集合 4 | ============= 5 | 6 | 7 | 适用范围 8 | ----------------------- 9 | 10 | 整数集合提供了这样一个抽象: 11 | 12 | 1. 集合可以保存 ``int_16`` 、 ``int_32t`` 和 ``int_64`` 三种不同长度的整数。 13 | 14 | 2. 对集合保存值所使用的字长是由程序自动调整的,这个过程被称为“升级”。 15 | 16 | 3. 所有整数在集合中都是独一无二的,各个整数以从小到达的顺序在集合中排序,所以程序在集合中查找元素的复杂度为 :math:`O(\log N)` 。 17 | 18 | 整数集合的详细信息和操作 API 可以参考 `《Redis 设计与实现》 `_ 的相关章节。 19 | 20 | 21 | 准备步骤 22 | ----------------------- 23 | 24 | 1. 从 Redis 源码中复制 ``endianconv.c`` 、 ``endianconv.h`` 、 ``intset.c`` 、 ``intset.h`` 、 ``zmalloc.c`` 和 ``zmalloc.h`` 到目标文件夹。 25 | 26 | 2. 将 ``#include "config.h"`` 从 ``zmalloc.c`` 中删除。 27 | 28 | 3. 将 ``#include `` 加入到 ``intset.h`` ,解决 ``size_t`` 未定义的问题。 29 | 30 | 31 | 测试驱动程序 32 | ----------------------- 33 | 34 | 以下程序对整数集合进行了三项测试: 35 | 36 | 1. 创建并删除一个空集合 37 | 38 | 2. 对集合进行添加、删除和查找操作 39 | 40 | 3. 检查整数集合的升级状态 41 | 42 | .. literalinclude:: code/intset/main.c 43 | 44 | 45 | 完成源码 46 | ------------------------ 47 | 48 | 测试程序的完整源码可以在 `这里 `_ 的 ``intset`` 文件夹找到。 49 | -------------------------------------------------------------------------------- /storage/reuse-redis-module/sds.rst: -------------------------------------------------------------------------------- 1 | .. highlight:: c 2 | 3 | SDS 模块 4 | ============= 5 | 6 | 7 | 适用范围 8 | ----------------- 9 | 10 | SDS 模块构建了一个字符串对象抽象, 11 | 这个抽象字符串具有以下特性: 12 | 13 | 1. 内容可以是二进制安全的 14 | 15 | 2. 可以在 :math:`O(1)` 复杂度内取出字符串的长度 16 | 17 | 3. 通过预分配内存来减少连续的追加(append)操作所需的内存重分配次数 18 | 19 | 如果你的程序需要以上这些特性, 20 | 那么可以考虑重用 Redis 的这个 SDS 模块。 21 | 22 | SDS 模块的具体定义,以及操作 API ,可以参考 `《Redis 设计与实现》 `_ 中的相关章节。 23 | 24 | 25 | 准备步骤 26 | ----------------- 27 | 28 | 1. 从 Redis 源代码中复制 ``sds.h`` 、 ``sds.c`` 、 ``zmalloc.h`` 和 ``zmalloc.c`` 四个文件到新建文件夹。 29 | 30 | 2. 添加 ``#include `` 到 ``zamlloc.h`` ,解决 ``size_t`` 未定义的问题。 31 | 32 | 3. 从 ``zamlloc.c`` 中移除 ``#include "config.h"`` ,因为现在已经不需要配置文件了。 33 | 34 | 35 | 测试驱动程序 36 | ----------------- 37 | 38 | 以下驱动程序展示了如何使用 SDS 模块, 39 | 并测试了其中的 ``sdsnew`` 、 ``sdslen`` 、 ``sdsavail`` 、 ``sdsdup`` 和 ``sdscat`` 等函数。 40 | 41 | .. literalinclude:: code/sds/main.c 42 | 43 | 44 | 完整源码 45 | ----------------- 46 | 47 | 重用程序的完整代码可以在 `这里 `_ 的 ``sds`` 文件夹中找到。 48 | -------------------------------------------------------------------------------- /storage/reuse-redis-module/ziplist.rst: -------------------------------------------------------------------------------- 1 | .. highlight:: c 2 | 3 | 压缩列表 4 | ===================== 5 | 6 | 7 | 适用范围 8 | --------------------- 9 | 10 | 压缩列表(ziplist)是一系列特殊编码的内存块, 11 | 它可以用节约内存的方式保存一系列字符串和整数值, 12 | 具体的信息请参考 `《Redis 设计与实现》 `_ 的相关章节。 13 | 14 | 15 | 准备工作 16 | --------------------- 17 | 18 | 1. 将以下文件复制到目标文件夹: 19 | 20 | - ``endianconv.c`` 21 | 22 | - ``endianconv.h`` 23 | 24 | - ``fmacros.h`` 25 | 26 | - ``util.c`` 27 | 28 | - ``util.h`` 29 | 30 | - ``ziplist.c`` 31 | 32 | - ``ziplist.h`` 33 | 34 | - ``zmalloc.c`` 35 | 36 | - ``zmalloc.h`` 37 | 38 | 2. 编辑 ``zmallo.c`` ,将其中的 ``#include "config.h"`` 语句删去。 39 | 40 | 41 | 测试驱动程序 42 | --------------------- 43 | 44 | 以下程序对 ``ziplist`` 模块进行了测试, 45 | 包括创建并释放一个空白的 ``ziplist`` , 46 | 以及对 ``ziplist`` 进行添加、删除和查找操作: 47 | 48 | .. literalinclude:: code/ziplist/main.c 49 | 50 | 51 | 完整源码 52 | --------------------- 53 | 54 | 完整的测试代码可以在 `这里 `_ 的 ``ziplist`` 文件夹找到。 55 | -------------------------------------------------------------------------------- /system/csapp/image/1.10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huangzworks/note/82ce594784a1f31095fc7a40cc0ab11e863c5db4/system/csapp/image/1.10.png -------------------------------------------------------------------------------- /system/csapp/image/1.11.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huangzworks/note/82ce594784a1f31095fc7a40cc0ab11e863c5db4/system/csapp/image/1.11.png -------------------------------------------------------------------------------- /system/csapp/image/1.13.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huangzworks/note/82ce594784a1f31095fc7a40cc0ab11e863c5db4/system/csapp/image/1.13.png -------------------------------------------------------------------------------- /system/csapp/image/1.14.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huangzworks/note/82ce594784a1f31095fc7a40cc0ab11e863c5db4/system/csapp/image/1.14.png -------------------------------------------------------------------------------- /system/csapp/image/1.17.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huangzworks/note/82ce594784a1f31095fc7a40cc0ab11e863c5db4/system/csapp/image/1.17.png -------------------------------------------------------------------------------- /system/csapp/image/1.18.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huangzworks/note/82ce594784a1f31095fc7a40cc0ab11e863c5db4/system/csapp/image/1.18.png -------------------------------------------------------------------------------- /system/csapp/image/1.4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huangzworks/note/82ce594784a1f31095fc7a40cc0ab11e863c5db4/system/csapp/image/1.4.png -------------------------------------------------------------------------------- /system/csapp/image/1.5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huangzworks/note/82ce594784a1f31095fc7a40cc0ab11e863c5db4/system/csapp/image/1.5.png -------------------------------------------------------------------------------- /system/csapp/image/1.6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huangzworks/note/82ce594784a1f31095fc7a40cc0ab11e863c5db4/system/csapp/image/1.6.png -------------------------------------------------------------------------------- /system/csapp/image/1.7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huangzworks/note/82ce594784a1f31095fc7a40cc0ab11e863c5db4/system/csapp/image/1.7.png -------------------------------------------------------------------------------- /system/csapp/image/1.8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huangzworks/note/82ce594784a1f31095fc7a40cc0ab11e863c5db4/system/csapp/image/1.8.png -------------------------------------------------------------------------------- /system/csapp/image/1.9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huangzworks/note/82ce594784a1f31095fc7a40cc0ab11e863c5db4/system/csapp/image/1.9.png -------------------------------------------------------------------------------- /system/csapp/image/compilation_system.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huangzworks/note/82ce594784a1f31095fc7a40cc0ab11e863c5db4/system/csapp/image/compilation_system.png -------------------------------------------------------------------------------- /system/csapp/image/hello_c_in_ascii.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huangzworks/note/82ce594784a1f31095fc7a40cc0ab11e863c5db4/system/csapp/image/hello_c_in_ascii.png -------------------------------------------------------------------------------- /system/csapp/index.rst: -------------------------------------------------------------------------------- 1 | 《Computer Systems : A Programmer's Perspective, 2e》笔记 2 | ========================================================== 3 | 4 | .. toctree:: 5 | :maxdepth: 2 6 | 7 | chp1 8 | -------------------------------------------------------------------------------- /system/on_building_systems_that_will_fail.rst: -------------------------------------------------------------------------------- 1 | On Building Systems That Will Fail 笔记 2 | ========================================= 3 | 4 | `Fernando J. Corbató `_ 领导并组织构建了 `CTSS `_ 和 `Multics `_ 系统。 5 | 6 | 因为在通用、大规模兼容分时和资源共享的计算机系统方面的卓越贡献,他获得了 1990 年的图灵奖,而《\ `On Building Systems That Will Fail `_\ 》就是他的获奖发言。 7 | 8 | 文章讲述了他在构建 CTSS 和 Multics 时的经历,关于如何构建系统、以及如何处理系统失效。 9 | 10 | 关于庞大的系统 11 | -------------------- 12 | 13 | 那些庞大的系统通常具有以下属性: 14 | 15 | 1. 规模庞大 16 | 17 | 2. 太复杂,以至于导致人手不足 18 | 19 | 3. 构建时间很长 20 | 21 | 4. (因为太昂贵)而不可能被放弃 22 | 23 | 5. 永远也完成不了! 24 | 25 | 6. 很少能完全像预想中那样工作,有时会以一种剧烈的方式出错 26 | 27 | 以上是一些负面的属性,下面是庞大的系统的一些好的方面: 28 | 29 | 7. 探索系统中的那些未知的部分令人振奋 30 | 31 | 8. 开辟出一个新的服务,或者新的领域,成为人们的必需品 32 | 33 | 9. 引发一系列后续创新 34 | 35 | 36 | 庞大的系统是如何出错的 37 | --------------------------- 38 | 39 | 构建庞大的系统非常困难,以下是一些可能造成失败的原因: 40 | 41 | 1. 使用正确的技术构建了错误的产品 42 | 43 | 2. 微妙的错误非常难以避免,甚至在某种程度上是不可避免的 44 | 45 | 3. 误解了程序模型,或者使用了错误的模型 46 | 47 | 4. 系统本身、或者系统依赖的底层系统改变得太快 48 | 49 | 5. 对于构建一个需要处理大量新问题的系统来说,错误是不可避免的 50 | 51 | 6. 在添加新特性时,忘记了旧有的假设,就会引发系统设计方面的 bug 52 | 53 | 7. 即使是有经验的程序员也会出错 54 | 55 | 56 | 复杂性的来源 57 | -------------- 58 | 59 | 构建系统时的复杂性来源于以下方面: 60 | 61 | 1. 人员的规模,以及由此产生出的信息传播失真,以及争论 62 | 63 | 2. 编程仍然是一个探索中的领域 64 | 65 | 3. 通常要基于假设对系统进行取舍 66 | 67 | 4. 底层系统的不完美 68 | 69 | 5. 要处理的条件太多 70 | 71 | 6. 没有一种好的语言来描述系统状况,统计数据常常被误用或者被误解 72 | 73 | 7. 变化太快 74 | 75 | 8. 程序的使用者 —— 人 —— 以及由之而来的复杂性 76 | 77 | 78 | 结论 79 | --------- 80 | 81 | 1. 要向前迈进,构建庞大的系统是不可避免的 82 | 83 | 2. 在庞大的系统中,错误是不可避免的 84 | 85 | 3. 简洁和优雅非常重要:用最少的机制和最大的清晰度来完成给定的功能 86 | 87 | 4. 使用隐喻:减少误解,加快理解,改善沟通,关注高层次而不是细节 88 | 89 | 5. 使用受限的语言:让程序员和设计师在给定的范围内讨论,不会产生不着边际的想法 90 | 91 | 6. 和人以及机器的错误作斗争,处理那些关键的失效错误部分 92 | 93 | 7. 在设计系统时,应该假设它会被持续的更新和修复,通过高维度的功能和结构模块化,来产生一个易于修改的系统。 94 | 95 | 8. 出错时,不要慌张,要找原因 96 | 97 | 9. 从错误中学习,同时对新解决方案的可能性保持警觉 98 | 99 | 10. 团队成员应该互相学习,互相教育 100 | -------------------------------------------------------------------------------- /thinking_and_learning/bakuman/chp20/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huangzworks/note/82ce594784a1f31095fc7a40cc0ab11e863c5db4/thinking_and_learning/bakuman/chp20/1.png -------------------------------------------------------------------------------- /thinking_and_learning/bakuman/chp20/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huangzworks/note/82ce594784a1f31095fc7a40cc0ab11e863c5db4/thinking_and_learning/bakuman/chp20/2.png -------------------------------------------------------------------------------- /thinking_and_learning/bakuman/chp20/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huangzworks/note/82ce594784a1f31095fc7a40cc0ab11e863c5db4/thinking_and_learning/bakuman/chp20/3.png -------------------------------------------------------------------------------- /thinking_and_learning/bakuman/chp20/4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huangzworks/note/82ce594784a1f31095fc7a40cc0ab11e863c5db4/thinking_and_learning/bakuman/chp20/4.png -------------------------------------------------------------------------------- /thinking_and_learning/bakuman/chp20/5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huangzworks/note/82ce594784a1f31095fc7a40cc0ab11e863c5db4/thinking_and_learning/bakuman/chp20/5.png -------------------------------------------------------------------------------- /thinking_and_learning/bakuman/chp20/6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huangzworks/note/82ce594784a1f31095fc7a40cc0ab11e863c5db4/thinking_and_learning/bakuman/chp20/6.png -------------------------------------------------------------------------------- /thinking_and_learning/bakuman/chp20/7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huangzworks/note/82ce594784a1f31095fc7a40cc0ab11e863c5db4/thinking_and_learning/bakuman/chp20/7.png -------------------------------------------------------------------------------- /thinking_and_learning/bakuman/chp20/8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huangzworks/note/82ce594784a1f31095fc7a40cc0ab11e863c5db4/thinking_and_learning/bakuman/chp20/8.png -------------------------------------------------------------------------------- /thinking_and_learning/bakuman/index.rst: -------------------------------------------------------------------------------- 1 | 《爆漫王》笔记 2 | ================== 3 | 4 | 第 2 卷 5 | ------- 6 | 7 | 第 8 章 8 | ^^^^^^^^^^^^^ 9 | 10 | 服部: 高木,你属于那种会思考怎么写才会受读者欢迎的人。 11 | 12 | 高木: 啊……是的。 13 | 14 | 服部:这或许只是我的个人想法,成功的漫画家,大致分为两类。 15 | 16 | - 一种是尽情地画自己想画的、喜欢画的东西,然后热卖。 17 | 说难听点这就是自我主义,说好听点就是天才型。 18 | 19 | - 另一种就是像你这种类型,经过缜密的构思计算,推出热卖作品。 20 | 21 | **画得出畅销作品的漫画家,绝大多数是未经过计算的人。** 22 | 23 | 服部:不好意思, 刚才我可能没有说清楚。 应该说靠分析计算,再创作漫画的做法,掌控起来难度比较高。反过来说,能靠分析计算推出畅销作品的人,才叫厉害。 24 | 25 | 第 11 章 26 | ^^^^^^^^^^^ 27 | 28 | 编辑长:漫画只要有趣就好。有趣的漫画被连载,是理所当然的事情。 29 | 30 | 第 16 章 31 | ^^^^^^^^^^^ 32 | 33 | 川口太郎: 34 | 35 | JUMP 上的漫画,每周都用这张读者问卷调差做排名。 36 | 37 | 真正的成败论英雄,正因此,每周都会觉得自己像被推上擂台的拳击手。 38 | 我的心情有喜有忧,完全被这读者问卷调查所左右。 39 | 从第二名到第二十名我都得过,唯独第一名,一次也没得过。 40 | 41 | 哪怕是得上一次也好啊,夺得第一名看看是什么滋味。 42 | 那是我从连载开始就天天做的梦。 43 | 44 | 45 | 第 3 卷 46 | ---------- 47 | 48 | 第 18 章 49 | ^^^^^^^^^^^^ 50 | 51 | 服部: 52 | 听好,新人中的大部分都是画王道。 53 | 他们不是刻意画王道,而是因为他们只接触过电子游戏和王道漫画,所以才会画那种题材。 54 | 可是,高木你能写出非王道的故事,这是你的武器。 55 | 56 | 第 20 章 57 | ^^^^^^^^^^^ 58 | 59 | 服部: 60 | 61 | 我毕竟是个编辑,能提的建议有限。 62 | 63 | 那些能做得比编辑要求的更多更好的人,才是优秀的漫画家。 64 | 你明白吧? 65 | 66 | 编辑保证不了写什么样的故事会畅销,说的极端一点,要是知道畅销作品创作的秘诀,自己画就好了。 67 | 68 | **漫画家一定要超越编辑才行!** 69 | 70 | 第 22 章 71 | ^^^^^^^^^^^^^^^^^^^ 72 | 73 | 福田: 74 | 新妻太缺乏职业漫画家的自觉。 75 | 如果只是想自己画得高兴,那么去画同人志就好啦。 76 | 可是,你是在 JUMP 上创作的职业漫画家,必须把如何让读者看得开心,当成第一要事。 77 | 78 | 真城: 79 | 你只靠天分就画得出来,对看过很多漫画的人来说,看你的漫画不过是看热闹,而在同时画漫画的人眼里,会觉得你很厉害…… 80 | 81 | 福田: 82 | 是的! 83 | “厉害”和“有趣”不同,读者如果不觉得故事有趣,作品就会人气大跌。 84 | 85 | 新妻: 86 | 唔……那我如何是好呢? 87 | 88 | 真城: 89 | 要画得简单易懂,让读者更能享受乐趣…… 90 | 91 | 第 23 章 92 | ^^^^^^^^^^^^ 93 | 94 | 福田: 95 | 不拿分镜稿给责任编辑看不行啊。 96 | 内容有没有趣,编辑凭什么判断啊? 97 | 因为麻烦而不画分镜稿,这种态度可以说是瞧不起职业漫画家这份工作。 98 | 99 | ---- 100 | 101 | 福田: 102 | 反正不能只顾自己画得高兴,要设想读者的心情来创作。 103 | 104 | 新妻: 105 | 哦……读者的心情吗? 106 | 107 | 真城: 108 | 要让读者在兴奋中期待后续,在牵挂中担心剧情。 109 | 110 | 福田: 111 | 每一话都要惊醒设计故事情节的高潮,用能诱使读者继续阅读的印子收尾。 112 | 113 | 第 32 章 114 | ^^^^^^^^^^^^^ 115 | 116 | 高木: 117 | 不过,立志当漫画家,结果是吉是凶,还不清楚哦。 118 | 119 | 亚豆: 120 | 怀抱梦想,努力追求,我想这本身不会带个人厄运。 121 | 122 | 123 | 第 5 卷 124 | ------------ 125 | 126 | 第 35 章 127 | ^^^^^^^^^^^ 128 | 129 | 港浦: 130 | 漫画能否受欢迎,不在于同别的作品比较,而在于作品本身是否有趣! 131 | 132 | 第 36 章 133 | ^^^^^^^^^^^^ 134 | 135 | 港浦: 136 | 画漫画就一定要认为它会受读者欢迎。 137 | 如果创作者不是因为作品有趣而创作,其作品也绝对不可能有趣。 138 | 读者从你们的作品中获得乐趣,你们以此为乐,才会继续创作出令读者更喜欢的故事。 139 | 140 | 第 37 章 141 | ^^^^^^^^^^^^^ 142 | 143 | 真城: 144 | 我想实现叔叔当年没有做到和没有完成的梦想。 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 | 第 11 卷 179 | ----------- 180 | 181 | 第 20 章 182 | ^^^^^^^^^^^ 183 | 184 | .. image:: chp20/1.png 185 | .. image:: chp20/2.png 186 | .. image:: chp20/3.png 187 | .. image:: chp20/4.png 188 | .. image:: chp20/5.png 189 | .. image:: chp20/6.png 190 | .. image:: chp20/7.png 191 | .. image:: chp20/8.png 192 | -------------------------------------------------------------------------------- /thinking_and_learning/pragmatic_thinking_and_learning/chp2.rst: -------------------------------------------------------------------------------- 1 | 第二章:从新手到专家的例程 2 | ============================== 3 | 4 | 5 | 德雷福斯模型(Dreyfus model)的五个阶段 6 | ------------------------------------------------ 7 | 8 | 德雷福斯兄弟定义了德雷福斯模型, 9 | 概括了从新手到专家必须经历的 5 个阶段, 10 | 包括: 11 | 12 | 1. 新手(novice) 13 | 14 | 2. 高级新手(advanced beginner) 15 | 16 | 3. 胜任者(competent) 17 | 18 | 4. 精通者(proficient) 19 | 20 | 5. 专家(expert) 21 | 22 | 他们的研究表明, 23 | 从新手到专家要经历巨大的变化。 24 | 25 | 在这个过程中, 26 | 人们不只是“获得了更多知识”, 27 | 或者学会了某些技术, 28 | 而且还在如何认识世界、 29 | 如何解决问题、 30 | 以及如何形成(form)和使用思维模型等方面体验到根本性的区别。 31 | 人们获取新技术的方式发生了改变, 32 | 影响(促进或阻碍)人们工作业绩的外部因素也发生了变化。 33 | 34 | 35 | 阶段一:新手 36 | ---------------- 37 | 38 | 新手的表现如下: 39 | 40 | 1. 在该技能领域经验很少,或者根本没有经验。 41 | 42 | 2. 想要立即知道答案,是对还是错。 43 | 44 | 3. 不是特别想要学习,他们想要一个立竿见影的目标。 45 | 46 | 4. 不知道如何应付错误,出错会让对他们造成慌乱。 47 | 48 | 通过给新手一份指令清单(recipe), 49 | 指示“如果发生 X ,那么执行 Y ”, 50 | 他们的效率可以显著地提高。 51 | 52 | 但是这种问题的做法是, 53 | 有时候新手可能不知道执行哪条规则的效果才是最好的; 54 | 除此之外, 55 | 规则无法做到尽善尽美, 56 | 当事件的情况超出规则之外时, 57 | 新手就会不知所措。 58 | 59 | 60 | 阶段二:高级新手 61 | ------------------- 62 | 63 | 高级新手的表现如下: 64 | 65 | 1. 开始多多少少摆脱固定的规则,可以独自尝试任务,但仍然难以解决问题。 66 | 67 | 2. 想要快速获取信息。 68 | 69 | 3. 能够根据过去的经验,逐步在正确的情境中采纳建议,但比较吃力。 70 | 71 | 4. 开始形成一些总体原则,但不是“全貌”。 72 | 73 | 5. 没有全面理解能力,而且也不想有;如果试图将一个更大的情境强加给他们,那么他们可能会认为该情境和原则不相关,从而将情境忽略掉。 74 | 75 | 76 | 阶段三:胜任者 77 | ---------------- 78 | 79 | 胜任者的表现如下: 80 | 81 | 1. 能够建立问题领域的概念模型,并有效地使用它们。 82 | 83 | 2. 可以独立解决自己遇到的问题,并开始考虑如何解决新问题 —— 那些他们之前没有遇到过的问题。 84 | 85 | 3. 开始寻求和运用专家的意见,并有效地利用。 86 | 87 | 4. 基于谨慎的计划和过去的经验来解决问题,如果没有更多的经验,在解决问题时他们将难以确定关注哪些细节。 88 | 89 | 胜任者通常在团队中发挥领导作用, 90 | 他们既可以指导新手, 91 | 也不会经常骚扰专家。 92 | 93 | 94 | 阶段四:精通者 95 | ------------------ 96 | 97 | 精通者的表现如下: 98 | 99 | 1. 他们需要全局思维,围绕这个技术,寻找并想了解更大的概念模型,对于简单化的信息,他们会非常沮丧。 100 | 101 | 2. 能够矫正以往不好的工作表现,他们会反思以前是如何做的,并修改其做法,期待下次表现得更好。到了这个阶段,自我改进才会出现。 102 | 103 | 3. 学习他人的经验,从故事中认真学习,即使没有亲自参与。 104 | 105 | 4. 能够理解、并在情境下使用格言、经验之谈、诀窍、谚语,等等(比如, Don't Repeat Yourself)。 106 | 107 | 5. 拥有足够的经验,可以按情况采取适当的行动,并对情况进行预测。 108 | 109 | 6. 知道较低水平的从业者所不知道的高级技巧。 110 | 111 | 112 | 阶段五:专家 113 | ---------------- 114 | 115 | 专家的表现如下: 116 | 117 | 1. 他们是各个领域知识和信息的主要来源。 118 | 119 | 2. 他们不断地寻找更好的方法和方式去做事。 120 | 121 | 3. 经验丰富,可以在恰当的情境中应用这些经验。 122 | 123 | 4. 根据直觉工作,而不需要理由。但是,他们通常很难解释清楚直觉(以及相关的结论)从何而来。 124 | 125 | 5. 擅长做有针对性的特征匹配:知道那些是无关紧要的细节,那些是非常重要的细节。 126 | 127 | 128 | 现实中的德雷福斯模型 129 | --------------------------------- 130 | 131 | **当你在某个领域不是很擅长的时候, 132 | 你更可能认为自己是这方面的专家。** 133 | 134 | 缺少准确的自我评估, 135 | 被称为二阶不胜任(second-order incompetence), 136 | 也即是,不知道自己不知道。 137 | 138 | 一旦你真的称为了一名专家, 139 | 你会痛苦地意识到你知道的是多么少。 140 | 141 | 专家给新手制定规则, 142 | 可以帮助新手显著地提升业绩, 143 | 但是, 144 | 如果要求专家按自己所指定的规则来行动, 145 | 则反而会影响专家自己的表现。 146 | 147 | 直觉是专家的工具, 148 | 而新手则需要明确的指示。 149 | 150 | 从新手到专家, 151 | 最重要的三个变化: 152 | 153 | .. image:: image/dreyfus-model-of-skill-acquisition.png 154 | 155 | 研究似乎表明, 156 | **大多数人的大多数技能, 157 | 在他们生命的大多数时间里, 158 | 从来没有高于第二阶段(高级新手)**\ : 159 | 很多人只是执行他们需要做的任务并根据需要学习新任务, 160 | 但是从来没有对任务环境获得更广泛的、概念上的理解。 161 | 162 | 元认知(metacognitive)能力, 163 | 或者自我认知的能力, 164 | 往往在比较高的技能层次才会具有, 165 | 处于低水平的人常常高估他们自己的能力, 166 | 而正确自我评估的唯一办法就是提高个人技能层次, 167 | 这反过来又会提高元认知能力。 168 | 169 | 170 | 有效地使用德雷福斯模型 171 | --------------------------- 172 | 173 | 积极的实践需要以下四个条件: 174 | 175 | 1. 需要一个明确定义的任务 176 | 177 | 2. 任务需要有适当难度 —— 有挑战、但可行 178 | 179 | 3. 任务环境可以提供大量反馈,以便你采取行动 180 | 181 | 4. 提供重复犯错和纠正错误的机会 182 | 183 | 培养高级新手, 184 | 帮助他们把水平提高到胜任者层次。 185 | 有助于实现这个目标的主要方法是在环境中有好的榜样, 186 | 让高级新手通过观察和模仿高水平的人来学习。 187 | 188 | 新手需要梯子, 189 | 但是优胜者不会在乎失败者 —— 失败者会被团队抛弃。 190 | 191 | 编程专家必须持续编程, 192 | 并找到一个有意义的、有价值的职业生涯, 193 | 通过实践来维持专家水平。 194 | 195 | 196 | 考虑情境 197 | ---------- 198 | 199 | 在进行判断时要将情境要考虑进去, 200 | 脱离情境的客观性存在固有危险。 201 | 202 | 举个例子,某种技术可能非常优秀,但如果你们团队里没有一个人了解这项技术,那么在使用这种技术之前就要三思而后行。 203 | 204 | 205 | -------------------------------------------------------------------------------- /thinking_and_learning/pragmatic_thinking_and_learning/chp3.rst: -------------------------------------------------------------------------------- 1 | 第三章:认识你的大脑 2 | ========================== 3 | 4 | 大脑的两种工作模式: 5 | 6 | - **线性模式(L 模式,或者 L 型)**\ :负责线性、逻辑思维和语言处理。相对缓慢,使用了大脑中相对较少的一部分资源。 7 | 8 | - **富模式(R 模式,或者 R 型)**\ :非线性、异步执行、综合处理。就像一个信号处理器,在“后台”计算,异步地返回结果。 9 | 10 | R 型对直觉、问题解决和创造性非常重要。 11 | 12 | L 型让你细致工作并实现目标。 13 | 14 | 两个模式互相干扰,不能同时使用。 15 | 16 | .. note:: 17 | 18 | 本质上不存在左脑思维和右脑思维这样的东西,大脑的各种脑页和不同层次的结构体之间的协作分布得非常均匀。 19 | 20 | L 型和 R 型只是指大脑所拥有的两种不同的认知风格。 21 | 22 | 这一概念在 Betty Edwards 的《Drawing on the Right Side of the Brain》中首次提出。 23 | 24 | .. note:: 25 | 26 | Linear 、Rich 和 Left 、Right 两个单词拥有同样的前缀字母只是巧合 —— L 型、 R 型理论和传统的左脑、右脑理论并不相同,前者是按功能区分的,而后者是按大脑的部位区分。 27 | 28 | 29 | L 型 30 | ------- 31 | 32 | L 型处理令人感到舒适、熟悉而轻松,它提供以下 9 种能力: 33 | 34 | 1. **语言能力** : 使用词语来命令、描述和定义。 35 | 36 | 2. **分析能力** : 有理有节分析事情。 37 | 38 | 3. **符号能力** : 用符号表示事物。 39 | 40 | 4. **抽象能力** : 抽取小部分信息(本质),并用其表示事物整体。 41 | 42 | 5. **时间能力** : 遵时循序。 43 | 44 | 6. **推理能力** : 基于理智和事实得到结论。 45 | 46 | 7. **数字能力** : 使用数字计数。 47 | 48 | 8. **逻辑能力** : 基于逻辑(定理、明确的论点)得出结论。 49 | 50 | 9. **线性思维能力** : 按照关联、依序推演问题和思考,经常会得出收敛性结论。 51 | 52 | 53 | R 型 54 | ----------- 55 | 56 | R 型特点听起来有一些奇怪、不协调甚至非常不舒服: 57 | 58 | 1. **非语言** : 可以获取语言,但是不能创建语言,它的思想无法通过语言来描述。 59 | 60 | 2. **综合** : 集合事物形成总体。 61 | 62 | 3. **具体** : 总是如实地反应事务。 63 | 64 | 4. **分析** : 使用类比来评价事物直接的关系。 65 | 66 | 5. **非理性** : 喜欢听好听的,而且不愿意为守时而费心。不受理性的约束,因为它不需要基于原因或者已知事实来处理输入 —— 因而,它完全愿意暂时不作任何判断。 67 | 68 | 6. **空间性** : 喜欢弄清楚事物之间的空间关系,了解部分如何形成整体。 69 | 70 | 7. **直觉** : 不用经过太多思考,很快就能出现的直接想法、感觉、信念或者偏好(来自\ `维基百科 `_\ )。 71 | 72 | 8. **全面** : 注重整体,总是希望一次就能看到事物整体,感知整体的模式和结构。 73 | 74 | 9. **非线性** : 直觉性的、跳跃性的思维,通常基于不完整的模式、直觉、归纳觉或者视觉影像来做判断。 75 | 76 | 77 | 从 L 型到 R 型 78 | -------------------- 79 | 80 | 我们需要更多地使用 R 型, 81 | 因为 R 型能提供直觉, 82 | 这是称为一名专家所迫切需要的。 83 | 没有它, 84 | 我们就不能称为专家。 85 | 86 | R 型提供了强大的综合学习能力。 87 | 88 | 模式匹配是专家表现的一项关键能力, 89 | 而 R 型提供了这种能力。 90 | 91 | 如果你想发现全局、整体的模式, 92 | 你需要 R 型;如果你需要分析部分和细节, 93 | 你需要 L 型。 94 | 95 | “非理性”并没有什么不对, 96 | 思维过程是非理性的或者不可重复的并不意味着它是不科学的、不负责任的、不合适的。 97 | 98 | 尽管 L 型思维方式的分析和语言能力带我们走了那么远, 99 | 但是我们已经因为过度依赖 L 型而失去了一些 R 型的重要能力。 100 | 为了前进, 101 | 为了推经人类发展的下一次革命, 102 | 我们需要学习将大大忽略的 R 型和 L 型重新集成。 103 | 104 | .. note:: 处于被禁锢状态下,生物的脑细胞会受到影响。 —— Elizabeth Gould 的实验研究 105 | 106 | .. note:: “真正想要了解一只青蛙,传统的解剖不是办法,更好的方式是构造一只青蛙。” —— 尼葛洛庞蒂,《Don't Dissect the Frog, Build It》。 107 | 108 | 例子 :设计胜于功能 109 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ 110 | 111 | 具有吸引力的用户界面要比不具吸引力(也即是,丑陋的)界面更易于使用, 112 | 尽管它们拥有相似的处理流程。 113 | 114 | 积极的情感对学习和创造性思维非常关键。 115 | 处于“高兴”的状态可以扩展你的思维过程, 116 | 激活更多的大脑物质。 117 | 118 | 与之相反,当你害怕或者生气时(充满了消极的情绪), 119 | 你的大脑开始停止提供多余的资源, 120 | 并未反抗或者逃跑做准备。 121 | 122 | .. tip:: 争取好的设计,它真的很有效。 123 | 124 | .. note:: “设计并不是创造美,美来自于选择、共鸣、同化和爱。” —— Louis Kahn 125 | 126 | 127 | 捕捉 R 型 128 | -------------- 129 | 130 | R 型通常是不可预测的, 131 | 你需要为此做好准备。 132 | 答案和灵感会独立于你的意识活动出现, 133 | 而且不是总在恰当的时候。 134 | 135 | 这意味着每周 7 天每天 24 小时需要随时准备好记录任何灵感和想法, 136 | 不论当时在做什么。 137 | 138 | 一旦开始记录这些方向, 139 | 你就会得到更多。 140 | 如果不使用这种方法, 141 | 大脑就会停止向你提供东西。 142 | 但是如果你开始使用它, 143 | 大脑就会非常乐意给给你提供比你想要的更多的东西。 144 | 145 | 每个人 —— 不论教育背景、经济状况如何, 146 | 不论日常工作是什么, 147 | 不论年纪大小 —— 都有好想法。 148 | 但是在这么多拥有好想法的人中, 149 | 只有少数人在努力跟踪它们。 150 | 而其中,又只有更少数人会努力辅助行动。 151 | 随后,仅有少之又少的人有能力将好想法成功实现。 152 | 153 | 要想达到下图的最顶层,必须跟踪好想法,这是最基本的要求: 154 | 155 | .. image:: image/idea.png 156 | 157 | 158 | 脑部的可重塑性 159 | ----------------- 160 | 161 | 直到最近, 162 | 人们还相信大脑的功能和内部“关联”从我们一出生就固定了。 163 | 也就是说, 164 | 大脑的各个局部趋于根据确定的规则执行相应的功能。 165 | 一部分皮层处理视觉输入, 166 | 另一部分处理味觉,等等。 167 | 这也意味着你所具有的做事能力和智力在出生时就基本确定了, 168 | 没有另外的训练或者开发可以使你超越着这个极限。 169 | 170 | 幸运的是,这种观点实际上是错误的 —— 人类大脑非常具有可塑性。 171 | 172 | 神经可塑性(大脑的可塑本质)也意味着你能够学习的最大容量或者你可以获得的技能数量不是固定的。 173 | 没有上限, 174 | 只要你相信这一点。 175 | 176 | 持续使用和时间的技能会组建占据大脑皮层中的统治地位, 177 | 这样一来, 178 | 大脑里就会有更多的部位被关联起来。 179 | 180 | 同时, 181 | 较少使用的技能会失去阵地。 182 | “不使用就会失去”, 183 | 这句话用在这里可谓恰如其分, 184 | 因为大脑会把更多的资源用于你做得更多的事情。 185 | -------------------------------------------------------------------------------- /thinking_and_learning/pragmatic_thinking_and_learning/image/dreyfus-model-of-skill-acquisition.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huangzworks/note/82ce594784a1f31095fc7a40cc0ab11e863c5db4/thinking_and_learning/pragmatic_thinking_and_learning/image/dreyfus-model-of-skill-acquisition.png -------------------------------------------------------------------------------- /thinking_and_learning/pragmatic_thinking_and_learning/image/idea.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huangzworks/note/82ce594784a1f31095fc7a40cc0ab11e863c5db4/thinking_and_learning/pragmatic_thinking_and_learning/image/idea.png -------------------------------------------------------------------------------- /thinking_and_learning/pragmatic_thinking_and_learning/index.rst: -------------------------------------------------------------------------------- 1 | 《程序员的思维修炼》 笔记 2 | =========================== 3 | 4 | 这是\ `《程序员的思维修炼 —— 开发认知潜能的九堂课》 `_\ 一书的笔记。 5 | 6 | .. toctree:: 7 | :maxdepth: 2 8 | 9 | chp2 10 | chp3 11 | --------------------------------------------------------------------------------